您的位置:首页 > 其它

list_head结构的使用

2014-09-17 23:15 190 查看
转载地址:http://blog.sina.com.cn/s/blog_5106eff10100xnxr.html

在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)

它在遍历之前,会先保存posnext域到n中,这样对pos节点删除后,下一个节点就不会断开了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: