文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>list_head结构的使用

list_head结构的使用

时间:2010-12-08  来源:wushuan10141

在Linux内核中,提供了一个用来创建双向循环链表的结构list_head。使用list_head提供的相应接口,链表操作将变得相当简单。

下面就是kernel中的list_head结构定义:
struct list_head {
        struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }

需要注意的一点是,头结点head是不使用的,这一点我在最初使用list_head的时候,经常忘记,直接把head当作第一个节点拿来使用,结果出错了,呵呵。记住!链表的第一个节点是head的下一个节点,呵呵。

下面简单介绍一下这个结构的几个常用接口:

LIST_HEAD() -- 生成一个名为name的双向链表头节点

#define LIST_HEAD(name) \
        struct list_head name = LIST_HEAD_INIT(name)

static inline void INIT_LIST_HEAD(struct list_head *list)
{
        list->next = list;
        list->prev = list;
}

list_entry()

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


根据指针ptr(该指针指向数据结构type中数据域member),返回其所在代表的整个数据结构(type)的指针
#define container_of(ptr, type, member)                     \
({                                                            \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);\
        (type *)( (char *)__mptr - offsetof(type,member) ); \
})

计算数据域MEMBER在数据结构TYPE中的偏移量
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

list_entry是个相当有用的接口,用多了你就会渐渐看出它的好了。因为list_head通常是作为某个struct的一个成员的,而不是单独拿来使用的,因此就经常需要用到list_entry了。

实例:
--------------------------------------------------
(1) ptr是一个page结构内部的成分lru的地址,而我们所需要的却是那个page结构本身的地址,所以要从地址ptr减去一个位移量,即成分lru内部的位移量,才能达到要求。那么,这位移量到底是多少呢?
        &((type *)0)->member
        &((struct page *)0)->lru        表示当结构体page正好在地址0上时其成分lru的地址,这就是位偏移量


(2) 根据给定的进程task,获取链表中的下一个进程(task->tasks.next指向下一个进程task_struct的tasks域,返回的数据结构为task_struct):
list_entry(task->tasks.next, struct task_struct, tasks)

task_struct {
    
        struct list_head tasks;
        ... ...

}

双向链表的插入操作 -- list_add()

将new所代表的结构体插入head所管理的双向链表的头节点head之后: (即插入表头)

static inline void list_add(struct list_head *new, struct list_head *head)
{
        __list_add(new, head, head->next);
}

new -->|---------------|
       |list_head *next|
       |list_head *prev|
       |---------------|
      
                                      (new 结点)
head-->|---------------|     |---------------|
       |list_head *next|---->|list_head *next|
       |list_head *prev|<----|list_head *prev|
       |---------------|     |---------------|
        
static inline void __list_add( struct list_head *new,
                                   struct list_head *prev,
                                   struct list_head *next)
{
       next->prev = new;         //(1)
       new->next = next;         //(2)
       new->prev = prev;         //(3)
        prev->next = new;         //(4)
}

__list_add()参数初始状态
-------------------------------------------------------
new ----->|---------------|
              |list_head *next|
              |list_head *prev|
              |---------------|
       
prev--+                   next--+
          |                         |    
head--+-->|---------------| |       |---------------|
              |list_head *next|-+-->|list_head *next|
              |list_head *prev|<----|list_head *prev|
              |---------------|         |---------------|
          
          
__list_add()运行后的链表状态 (将新节点new插入到头节点head之后)     
---------------------------------------------------------       
prev--+                    next---+
          |                           |    
head--+-->|---------------|<-+|        |---------------|
              |list_head *next|-+|+--->|list_head *next|
              |list_head *prev| ||+----|list_head *prev|
              |---------------| ||| +->|---------------|
       +------------------------+|| |
       |            (3)                  || |          
       |--+----------------------+| |
       |--|                        (1)| |
new|--|-->|---------------|<--+ |
       |--|       |list_head *next|-----+(2)
       |--|---|list_head *prev|
       +----->|---------------|
         (4)

如果是要插入链表末尾,可以使用list_add_tail(new, head);

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
     __list_add(new, head->prev, head);
}

list_del(entry)      把entry从list中删除

static inline void list_del(struct list_head *entry)
{
     __list_del(entry->prev, entry->next);
     entry->next = LIST_POISON1;
     entry->prev = LIST_POISON2;
}

static inline void __list_del(struct list_head * prev, struct list_head * next)
{
     next->prev = prev;
     prev->next = next;
}

list_empty() -- 判断链表是否为空

如果双向链表head为空则返回真,否则为假
----------------------------------------------------
static inline int list_empty(const struct list_head *head)
{
        return head->next == head;
}

遍历双向链表,list_head提供了两个很有用的宏定义:

遍历双向链表 -- list_for_each()

遍历head(struct list_head)索引的双向链表,每找到一个节点就将该节点赋给pos(struct list_head)

#define list_for_each(pos, head) \
        for (pos = (head)->next; prefetch(pos->next), pos != (head); pos = pos->next)
            
                       pos ---+
                              |
head -->|---------------| | -|---------------|        |---------------|
            |list_head *next|-+->|list_head *next|--->|list_head *next|
            |list_head *prev|<---|list_head *prev|<---|list_head *prev|
            |---------------|        |---------------|        |---------------|

如果只是遍历读取list的话,直接使用list_for_each()即可。可是如果你在遍历过程中还要对list进行操作的话(特别是delete操作),应使用另一个安全版本list_for_each_safe()。

list_for_each_safe()

#define list_for_each_safe(pos, n, head) \
        for (pos = (head)->next, n = pos->next; pos != (head);      pos = n, n = pos->next)

它在遍历之前,会先保存pos的next域到n中,这样对pos节点删除后,下一个节点就不会断开了。


相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载