首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

操作系统内核经常需要维护数据结构。内核有标准的循环链表、双向链表的实现。在<Linux/list.h>文件中定义了一个list_head类型简单结构:

 

struct list_head {

   struct list_head *next, *prev;

};

 

通用链表的常用用途是将某一个数据结构本身串成链表,或将某些链表与一个数据结构联系起来,这两种情况实质上都是由结构list_head组成链表,只是list_head所“背负”的负载不一样。下面分别举例说明这两种用途。

以下示例说明了如何将某一个数据结构本身串成链表,并对链表进行操作,同时还说明list_head结构的实现与使用。

示例:将某一个数据结构本身串成链表。

1)加入list_head结构成员。

假设有一个example_struct结构需连接成链表,因而在其结构里面加上list_head成员,就组成了结构链表,如下:

 

struct example_struct {

   struct list_head list;

   int priority; 

   ……//其他成员

};

 

example_struct结构中的list成员,用来将example_struct结构串成链表。可理解为list_head“背负”的负载是example_struct结构。

2)创建list_head结构。

使用前必须申请链表头并用 INIT_LIST_HEAD 宏来初始化链表头。可使用两种方法。

方法1

 

struct list_head example_list;

INIT_LIST_HEAD(&example_list);

 

方法2

 

LIST_HEAD(example_list);

 

其中,这两个宏在include/Linux/list.h中定义如下:

 

#define LIST_HEAD(name) \

     struct list_head name = LIST_HEAD_INIT(name)

 

#define INIT_LIST_HEAD(ptr) do { \

     (ptr)->next = (ptr); (ptr)->prev = (ptr); \

} while (0)

 

宏定义INIT_LIST_HEAD初始化了链表头,即向前、向后的指针都指向链表头。这样,就已初始化了一个example_list的链表头,以后就可以向链表中增加链表元素了。

3)链表与用户结构连接。

list_entry宏将exmplelist链表与exmple_struct结构类型连接起来,其原理如图1.1所示。

有两项链表的链表头

List_entry宏的效果

list_head的定制结构

next

prev

list_head结构

空链表

<linux/list. h>中的链表

1.1  list_entry宏及struct list_head链表的关系图

下面这个代码行就是从examplelist链表中得到节点对应的example_struct结构指针,其中ptrexamplelist链表中的指针,如ptr = examplelist->next

 

struct example_struct *node =

   list_entry(ptr, struct example_struct, list);

 

在上面代码行中的宏定义list_entry将一个 list_head结构指针映射回一个指向结构example_struct的指针,即得到list_head的宿主结构下面分析这个宏定义列出如下(在include/Linux/list.h中):

 

#define list_entry(ptr, type, member) \

     container_of(ptr, type, member)

 

list_entry的功能是得到链表中节点的结构,它的参数含义为:

ØØ  ptr是链表中的一个struct list_head结构元素指针。

ØØ  type:  用户定义的结构类型,其中,包含struct list_head结构成员。

ØØ  member用户定义结构中的struct list_head结构成员名字。

include/Linux/kernel.h中有container_of的定义,参数含义与list_entry中一致,container_of得到list的容器结构,即含有list成员的结构typecontainer_of的定义如下:

 

#define container_of(ptr, type, member) ({              \

    //将链表中的元素ptr转换成结构type中成员member的类型

        const typeof( ((type *)0)->member ) *__mptr = (ptr);  \

    //__mptr减去member成员偏移地址正好是type结构地址

        (type *)( (char *)__mptr - offsetof(type,member) );})

 

include/Linux/stddef.h中有宏offsetof的定义,列出如下:

 

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

 

offsetof宏对于上述示例的展开及分析是:&((struct example_struct *)0)->list表示当结构example_struct正好在地址0上时其成员list的地址,即成员位移。

4)遍历链表

下面使用list_entry 宏遍历链表得到链表指针,再从链表指针映射回对应结构example_struct的指针。然后,对其成员priority进行操作。函数example_add_entry的功能是给链表加入新的结构成员。

 

void example_add_entry(struct example_struct *new)

{

   struct list_head *ptr;

   struct example_struct *entry;

  //遍历链表

   for (ptr = exmple_list.next; ptr != &exmple_list; ptr = ptr->next) {

       //映射回对应结构example_struct的指针

       entry = list_entry(ptr, struct todo_struct, list);

       if (entry->priority < new->priority) {

           list_add_tail(&new->list, ptr);

           return;

       }

   }

   list_add_tail(&new->list, &exmple_struct)

}

 

示例:2将某些链表与一个数据结构联系起来。

函数new_inode为给定的超级块分配一个新的节点,并将新的节点加到链表inode_in_usesb->s_inodes中,从而形成了两个链表中链接了新的节点。一个是以inode_in_use为链表头的全局的节点链表;一个是超级块结构为链表头的节点链表。其中,超级块与节点链表连接如图1.2所示。

 

 

fs/inode.c

extern struct list_head inode_in_use;

struct inode *new_inode(struct super_block *sb)

{

     static unsigned long last_ino;

     struct inode * inode;

 

     spin_lock_prefetch(&inode_lock);

    

     inode = alloc_inode(sb);

     if (inode) {

         spin_lock(&inode_lock);

         inodes_stat.nr_inodes++;

     //inode链表加到inode_in_use链表

         list_add(&inode->i_list, &inode_in_use);

         list_add(&inode->i_sb_list, &sb->s_inodes); //inode加到超级块的节点链表中

         inode->i_ino = ++last_ino;

         inode->i_state = 0;

         spin_unlock(&inode_lock);

     }

     return inode;

……

}

 

 

 

1.2  超级块与节点链表连接图

include/Linux/list.h中还定义了下面操作链表的函数。

ØØ list_add(struct list_head *new, struct list_head *head)这个函数在链表头后面添加新元素入口——。如果通常是在链表的头部添加元素,就。这样,它可以用来建立栈。需要注意的是,head并不一定非得是链表的第一项,如果传递了一个恰巧位于链表中间某处的list_head结构,新入口会立即排在它的后面。因为Linux链表是循环的,链表头通常与其他入口没有本质区别。

ØØ list_add_tail(struct list_head *new, struct list_head *head);在给定链表头的前面增加一个新的元素入口,即在链表的末尾添加。因此,可使用list_add_tail建立“先入先出”队列。

ØØ list_del(struct list_head *entry);从链表中将给定的入口删除。

ØØ list_empty(struct list_head *head);如果给定的链表是空的,就返回一个非零值。

ØØ list_splice(struct list_head *list, struct list_head *head);这个函数通过在head的后面插入list来合并两个链表。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论