0x00 前言
linux中的双向链表和传统的双向链表不太一样,是注入了抽象封装灵魂的链表,
他不是把将数据结构塞入链表,而是将链表节点塞入数据
之前在windows里就见识过_ETHREAD结构.....的双向链表,但苦于没有源码,所以在linux里好好康一康。
链表的头文件是list.h(include\linux\list.h),我们重此来探究
参考文章:
https://blog.csdn.net/xu_ya_fei/article/details/49744699
https://www.cnblogs.com/wangzahngjun/p/5556448.html
https://wenku.baidu.com/view/5df5736fa0116c175e0e4817.html
https://blog.csdn.net/wanshilun/article/details/79747710
https://www.ibm.com/developerworks/cn/linux/kernel/l-chain/index.html
0x01 传统双向链表
详见此文http://data.biancheng.net/view/8.html
就不在此赘述了。
0x02 基础数据结构
1. 内核链表的定义
struct list_head {
struct list_head *next, *prev;
};
可以看出来,linux内核里并没有数据域,这正是体现抽象思想的表现,把双向链表的共性(前驱指针,后继指针)作为一个基本数据结构。
所以我们就可以如下使用
struct My_List{
void* My_Data;
struct list_head My_List;
};
1) list_head 是一种侵入式链表,数据附加在链表之上,使得 list_head 数据结构是通用的,使用起来就不需要考虑节点数据的类型。
2)list_head结构体可以添加到结构体的任何位置
3)可以为 list_head 命名
4)可以添加多个list_head链表
2.是否有效
#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
通常在进行插入和删除时会首先判断是否有效
3.WRITE_ONCE && READ_ONCE
在某些情况下CPU对内存中变量读写并不是一次完成的,这可能会出现竞争。而READ_ONCE和WRITE_ONCE实现对变量一次性读取和一次性写入。
详情可以看这篇文章:https://blog.csdn.net/cloudblaze/article/details/51676139
4. offsetof && container_of
在下文 "遍历" 时详细说明。
0x03 基础函数
1. 声明与初始化
上一段描述了链表的链节点,在LIST_HEAD这个宏中描述了头节点
在源码中可以看到俩个宏:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
第一个:&(name) 前驱指针,后继指针都是指向自己的指针
第二个:调用了第一个的初始化,使用宏 LIST_HEAD_INIT 进行初始化,这会使用变量name 的地址来填充prev和next 结构体的两个变量
两者的使用的不同:第一个宏是初始化,第二个宏是声明+初始化
除了宏以外还有Linux还提供了一个INIT_LIST_HEAD宏用于运行时初始化链表
static inline void
INIT_LIST_HEAD(struct list_head *list)
{
list->next = list->prev = list;
}
2. 插入
在linux内核中一共有俩个插入的函数
static inline void list_add()
static inline void list_add_tail()
list_add在链表头插入,实现栈的功能
list_add_tail在链表尾