<强>描述强>
在linux内核中封装了一个通用的双向链表库,这个通用的链表库有很好的扩展性和封装性,它给我们提供了一个固定的指针域结构体,我们在使用的时候,只需要在我们定义的数据域结构体中包含这个指针域结构体就可以了,具体的实现,链接并不需要我们关心,只要调用提供给我们的相关接口就可以完成了。
传统的链表结构
结构节点{ int的关键; int val; 节点*:; 节点*下; }
<强> linux内核通用链表库结构强>
提供给我们的指针域结构体:
struct list_head { struct list_head *, *:;};
我们只需要包含它就可以:
结构节点{ int val; int的关键; struct list_head *头; }
可以看到通过这个list_head结构就把我们的数据层跟驱动层分开了,而内核提供的各种操作方法接口也只关心list_head这个结构,也就是具体链接的时候也只链接这个list_head结构,并不关心你数据层定义了什么类型。
一些接口宏定义
//初始化头指针 #定义LIST_HEAD_INIT(名字){,(名字),和(名字)} #定义LIST_HEAD \(名字) struct list_head name=LIST_HEAD_INIT(名字)//遍历链表 #定义__list_for_each (pos) \ (pos=(头)→下;pos !=(头);pos=pos→下一个)//获取节点首地址(不是list_head地址,是数据层节点首地址) #定义list_entry (ptr、类型成员)\ container_of (ptr、类型成员)//container_of在Linux内核中是一个常用的宏,用于从包含在某个//结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变//量中某个成员的首地址进而获得整个结构体变量的首地址 #定义container_of (ptr、类型成员)({\ const typeof(((*)型0)→成员)* __mptr=(ptr);\ (*)((char *) __mptr - offsetof(类型、成员));}) #定义offsetof (s, m) (size_t),(((*) 0)→米) >之前使用方式
typedef结构节点{ int val; int的关键; struct list_head *列表; }节点;//初始化头指针 LIST_HEAD(头);//创建节点 节点*=malloc (sizeof(节点)); 节点* b=malloc (sizeof(节点));//插入链表方式一 list_add(和→列表,及头); list_add(和b→列表,及头);//插入链表方式二 list_add_tail(和→列表,及头); list_add_tail(和b→列表,及头);//遍历链表 struct list_head * p; 结构节点* n; 负责人__list_for_each (p) {//返回list_head地址,然后再通过list_head地址反推//节点结构体首地址。 n=list_entry (pos、结构节点列表); } >之前list_add接口,先入后出原则,有点类似于栈
list_add——先入后出模式
list_add_tail接口,先入先出原则,有点类似于fifo
list_add——先入先出模式
我们的链表节点,实际在内存中的展示形态
节点描述
可以看到最终的形态是,通过指向每个结构体里面的list_head类型指针,然后把它们串联起来的
list_entry接口,通过结构体变量某个成员的地址,反推结构体首地址,就像__list_for_each接口只返回list_head地址,所以我们要通过这个成员地址在去获取它本身的结构体首地址,底层实现方法container_of宏
反推结构体首地址
举个例子
这个例子包括简单的增,删,遍历
# include & lt; linux/kernel.h> # include & lt; linux/module.h> # include & lt; linux/init.h> # include & lt; linux/slab.h> # include & lt; linux/list.h> MODULE_LICENSE (GPL); MODULE_AUTHOR(“大卫·谢”); MODULE_DESCRIPTION(“列表模块”); MODULE_ALIAS(“列表模块”); struct学生//代表一个实际节点的结构 { char[100]名称; int num; struct list_head列表;//内核链表里的节点结构 }; struct学生* pstudent; struct学生* tmp_student; struct list_head student_list; struct list_head * pos; int mylist_init(空白) { int i=0;//初始化一个链表,其实就是把student_list的上一页和下一页指向自身 INIT_LIST_HEAD(及student_list); pstudent=kmalloc (sizeof (struct学生)* 5,GFP_KERNEL);//向内核申请5个学生结构空间 memset (pstudent 0 sizeof (struct学生)* 5);//清空,这两个函数可以由kzalloc单独做的到 (i=0; iLinux内核通用链表学习小结