代码文件deepin15.10/include/list.h
linux内核提供了一个双向链表数据结构,同时包括一系列接口。
在list.h里包含的头文件linux/types.h里找到了链表结构体的定义:
struct list_head {
struct list_head *next, *prev;
};
形象一点的逻辑结构可以表示为:
奇怪的是居然没有数据域(一般的链表包括两个部分,一个是数据域,也就是存放数据的部分,和指针域,用来存放其他节点地址的部分,可能后面还有蹊跷,慢慢往后研究)
结构体里包含两个成员,两个成员都是list_head的指针。分别指向这一节点的前驱(prev)和后继(next)。
在list.h文件里定义的第一个宏
#define LIST_HEAD_INIT(name) { &(name), &(name) }
这个宏定义初看很难懂,LIST_HEAD_INIT()函数传入一个叫name的变量,函数的功能是取变量name的地址。从函数名看出这是链表的初始化。但看了第二句宏定义就豁然开朗了。
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
在这句里宏定义一个函数LIST_HEAD,函数创建一个链表节点name,并且将name的前驱和后继都指向自己。
下面声明了第一个函数:
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
目前我还不清楚这个函数和前一个宏定义的具体区别,是让list链表的前驱和后继都指向list自己。函数WRITE_ONCE()定义在include/linux/compiler.h里(这个函数涉及编译时的内存操作,和后面的READ_ONCE()一样都是为了cpu读取和访问内存时候的操作安全)
下面的代码都是在Debug模式下检查链表有效性的:
#ifdef CONFIG_DEBUG_LIST
extern bool __list_add_valid(struct list_head *new,
struct list_head *prev,
struct list_head *next);
extern bool __list_del_entry_valid(struct list_head *entry);
#else
static inline bool __list_add_valid(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
return true;
}
static inline bool __list_del_entry_valid(struct list_head *entry)
{
return true;
}
#endif
文件之后定义了一系列操作链表节点的接口,逐一看。
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
这个函数传递三个参数,分别是插入节点的地址,插入位置的前后节点的地址:
比如调用函数_list_add(&new, &(n), &(n+1)),执行结果是在节点n和节点n+1之间插入节点new:
第一步:节点n+1的prev指向节点new的地址0x06f
第二步:节点new的next指向节点n+1的地址0x00a
第三步:节点n的next指向节点new,节点new的prev指向节点n
未完。。。