1. 内核中双向链表结构
Linux内核中广泛使用了一种称为循环双链(Circular Doubly Linked)链表的数据结构,即每个节点都有一个向前和向后的链接,而且首尾节点也相互连接。使用这样的链表,你可以很方便地访问任何一个元素的上一个和下一个元素,且无论目前位于哪个元素。
循环双链表的节点通常用struct list_head
结构来表示,其定义如下:
struct list_head {
struct list_head *next, *prev;
};
其中,next
指向链表中的下一个元素,prev
指向上一个元素。
要在你自己的结构中使用链表,通常在你的结构体中包含一个struct list_head
类型的变量。例如:
struct my_struct {
int my_number;
// 这是一个链表节点,将用于将你的结构链接到链表中
struct list_head list_node;
}
ps:本质就是在自己定义的结构体中嵌入一个struct list_head类型的变量,注意这里不是嵌入结构体指针,而是嵌入结构体本身; 还有这个自己定义的结构体并不是说用户程序定义的,既然是给内核使用的,那必然是内核程序定义的,或者内核代码定义的。
这样,就可以使用Linux内核提供的链表函数,例如 list_add()
(在链表中添加新节点)、list_del()
(删除节点)、list_empty()
(检查链表是否为空)等,来操作链表。
注意:在你的代码中,通常不直接操作next
和prev
指针。相反,你应当使用内核提供的链表函数,因为这些函数已经对各种边缘情况进行了处理,可以确保你的链表操作是正确且安全的。
举例说明
假设你的数据结构如下:
struct my_struct {
int value;
struct list_head list_node;
};
在这种情况下,你可以使用如下方式将自定义数据结构添加到链表中:
struct my_struct item1;
INIT_LIST_HEAD(&item1.list_node);
...
struct my_struct item2;
list_add(&item2.list_node, &item1.list_node);
这里,list_add
将item2
的list_node
加入到item1
的list_node
后面。当然,list_add
函数其实只关心list_node
部分,它不知道这个节点是与什么类型的数据结构关联。
你可能会问:那么当我从链表中获取一个节点时,我如何找回它所关联的数据结构呢?
答案是:使用list_entry
宏。例如:
struct list_head *ptr = ...; // 假设指向你想要的链表节点
struct my_struct *entry = list_entry(ptr, struct my_struct, list_node);
这里,list_entry
宏将指针从list_node
成员转换为包含它的整个结构体
。这个转换是通过计算偏移量
完成的。list_entry
需要你提供链表节点的指针、包含该节点的数据结构的类型、以及链表节点在该结构体中的名称。结果是一个指向整个数据结构的指针。