linux内核数据结构之双向循环链表struct list_head
链表对每位写过程序的同学都再熟悉不过了,无非是对链表的创建、初始化、插入、删除、遍历等操作。但您是否想过如果针对每一种数据结构都实现一套对链表操作的服务原语是否太浪费时间和精力了,实际上在linux内核2.4以后内核开发者对链表的结构实现了一个统一的接口,可以利用这些接口实现链表而不用去考虑数据结构的差异。您的兴趣是否来了.....那就让我们一睹为快吧。
--------------------------------原理篇-------------------------------------
链表数据结构的定义很简单(节选自[include/linux/list.h],以下所有代码,除非加以说明,其余均取自该文件):
struct
};
我是初学者时会这样构造数据:
struct
};
这样我们把数据嵌入到链表节点中示意图:
而内核的数据结却是将链表的节点嵌入到数据结构中:
struct
};
链表的操作是通过访问每一个list_head来操作的。
有两个定义可供参考:
(1)参考定义
在应用中:
ptr是指向list_head类型链表的指针
type为一个包含list_head结构的结构类型
member为结构中类型为list_head的域
如果data现在在0地址上,那么&(type
2)上面的只是一个版本,略懂。。略懂。。之后然我们看看实际的list.h文件中定义的list_entry()。
实际定义
#define
#define
#define
--------------------------实践篇------------------------------------------------------------------
既然原理看懂了,那我们也的实际操作一下毕竟实践是验证真理的唯一标准。
操作前先来看看两个宏:
#define
#define
我们来分析第二个宏,定义一个以name为名字的节点,并将节点初始化如图:
是的正如你所想这个宏定义了一个空节点,节点的next,prev都指向节点本身,相当于:
name->prev
name->next
list_add(new,
list_add_tail(new,
list_del(entry);
list_empty(head);
list_entry(ptr,
list_for_each(pos,
//下面这个最有用!
list_for_each_entry(pos,
。。。。。。
还有其他的定义都可以在include/linux/list.h中找到
需要强调的是:
#define
#define
这里的prefetch()还没理清,将在后续文章中写出。
写个小例子说明用法:
<span style="background-color: rgb(255, 255, 255);">#include <stdio.h>
#include <stdlib.h>
#include "list.h" //因为我没有<linux/list.h>是从内核中copy过来的list.h放在本地文件夹下
//注意你用到哪个定义就copy那个定义
struct test_list{
int data;
struct list_head list;
};
int main()
{
LIST_HEAD(boy); //define and initialize a doubly linked of boy
int i;
struct test_list *ptr[10];
struct list_head *tmp = NULL;
struct test_list *node;
for(i = 0; i < 10; i++)
{
ptr[i] = (struct test_list *)malloc(sizeof(struct test_list));
if(!ptr[i])
printf("error");
ptr[i]->data = i;
}
for(i = 0; i < 10; i++)
{
list_add_tail(&ptr[i]->list, &boy);
}
printf("Traverse link:\n");
list_for_each(tmp, &boy) //boy为链表的头节
{ // tmp为list_head类型的指针用来暂时存放节点
node = list_entry(tmp, struct test_list, list); //node为包含list_head的结构这里类
//型为struct test_list 即如前面所讲
//知道了链表的节点(tmp)就可以找
//到包含节点结构的地址
printf("%d ", node->data);
}
printf("\n");
return 0;
}</span>
运行结果:为了方便我测试用的list.h在下边给出
<span style="background-color: rgb(255, 255, 255);">#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct list_head{
struct list_head *next;
struct list_head *prev;
};
#define LIST_HEAD_INIT(name) {&(name), &(name)}
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
static inline int list_empty(struct list_head *head)
{
return head->next == head;
}
static inline void __list_add(struct list_head *new,
struct list_head *prev,struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
#endif</span>
参考资料:
Linux内核设计与实现(原书第三版第六章)
在线文档https://www.ibm.com/developerworks/cn/linux/kernel/l-chain/