linux kernel 中的链表
本文总结下最近学习的 kernel 中链表的使用和实现细节,本章主要是总结下链表部分提供的常用接口。
kernel 数据结构风格
linux kernel 在实现中,沉淀了不少的基础库,其中数据结构的封装和实现让人感觉非常的巧妙,而且用起来觉得非常的强大。
kernel 在实现一个通用的数据结构时,通常都是不带数据部分的,这点有些像C++了,他在给出一个数据结构时,只会给出结构本身的基础操作,对“业务”逻辑基本就是提供一个接口,让开发者自己搭积木。
链表的定义
struct list_head {
struct list_head *next, *prev;
}
链表的定义放在 linux/list.h
里面,定义很就是俩指针,由于他不带数据字段,需要开发者自己添加,比如就存个
typedef struct kv {
int key, value;
} KV;
typedef struct mylist {
struct list_head listnode;
KV data;
} MYLIST;
链表的基础操作
kernel 中对于链表定义了很多基础操作,常用的主要有:
static inline void list_add(struct list_head *new, struct list_head *head); \\ 从链表头插入一个节点
static inline void list_add_tail(struct list_head *new, struct list_head *head); \\ 从链表尾插入一个节点
static inline void list_del(struct list_head *entry); \\ 删除某个节点
static inline void list_move(struct list_head *list, struct list_head *head); \\把一个节点移动到第一个
static inline void list_move_tail(struct list_head *list, struct list_head *head); \\ 把一个节点移动到最后一个位置
static inline void list_splice(struct list_head *list, struct list_head *head); \\合并两个链表(把 list 挂到 head 后面)
链表的常用宏
封装的内容分析下一章在描述,基本有这些就能完成一些普通的链表操作了。不过为了更顺畅的 coding,kernel 很人性化的做了很多基础使用宏。
/*
* 初始化宏,作用是声明一个链表结构,并且让他的前驱和后继指针都指向自己
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)、
/*
* 初始化宏,作用是在使用过程中,再次初始化让他的前驱和后继指针都指向自己
*/
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* 获取实例宏,kernel 里最经典的宏之一,几乎所有的数据结构都需要用上这个宏。
* 宏的用法是给出某个结构指针,并且给出他被包含的结构体类型以及成员名称,返回该外包结构体的实例指针
*/
#define list_entry(ptr, type, member) container_of(ptr, type, member)
/*
* 遍历宏
* 第一个是用来遍历结构体本身
* 由于每次遍历结构体本身大都涉及到取出其数据段,所以设计了第二条宏直接循环整个数据结构实例
*/
#define list_for_each(pos, head) \
for (pos = (head)->next, prefetch(pos->next); pos != (head); \
pos = pos->next, prefetch(pos->next))
#define list_for_each_entry(pos, head, member) ……
以上基本就是链表结构提供出来的接口和方法,基于这些基本就可以自己动手玩一玩链表了,整体来说难度不大,写的很精练,也是理解很多 kernel 数据结构的基础。 linux/list.h
里面还放了一个 hlist
,是用来做 hash 链表的,下一章主要总结下 hlist 。