list_head结构的使用

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 节点删除后,下一个节点就不会断开了。


你可能感兴趣的:(数据结构,list,struct,delete,each,linux内核)