在解释完内核中的链表基本知识以后,下面解释链表的重要接口操作:
1. 声明和初始化
实际上Linux只定义了链表节点,并没有专门定义链表头,那么一个链表结构是如何建立起来的呢?让我们来看看LIST_HEAD()这个宏:
#define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
需要注意的是,Linux 的每个双循环链表都有一个链表头,链表头也是一个节点,只不过它不嵌入到宿主数据结构中,即不能利用链表头定位到对应的宿主结构,但可以由之获得虚拟的宿主结构指针。
LIST_HEAD()宏可以同时完成定义链表头,并初始化这个双循环链表为空。
静态定义一个list_head 类型变量,该变量一定为头节点。name为struct list_head{}类型的一个变量,&(name)为该结构体变量的地址。用name结构体变量的始地址将该结构体变量进行初始化。
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
动态初始化一个已经存在的list_head对象,ptr为一个结构体的指针,这样可以初始化堆栈以及全局区定义的list_head对象。ptr使用时候,当用括号,(ptr),避免ptr为表达式时宏扩展带来的异常问题。此宏很少用于动态初始化内嵌的list对象,主要是链表合并或者删除后重新初始化头部。若是在堆中申请了这个链表头,调用INIT_LIST_HEAD()宏初始化链表节点,将next和prev指针都指向其自身,我们就构造了一个空的双循环链表。
在宏的下面有一个内联函数:
static inline void INIT_LIST_HEAD(struct list_head *list)
29 {
30 list->next = list;
31 list->prev = list;
32 }
这样便可初始化头节点
当我们用LIST_HEAD(nf_sockopts)声明一个名为nf_sockopts的链表头时,它的next、prev指针都初始化为指向自己,这样,我们就有了一个空链表,因为Linux用头指针的next是否指向自己来判断链表是否为空:
static inline int list_empty(const struct list_head *head) { return head->next == head; }
2. 插入/删除/合并
a) 插入
在上面的设计下,所有链表(包括添加、删除、移动和拼接等)操作都是针对数据结构list_head进行的。提供给用户的的添加链表的操作有两种:表头添加和表尾添加。注意到,Linux双循环链表中有一个链表头,表头添加是指添加到链表头之后,而表尾添加则是添加到链表头的prev所指链表节点之后。
对链表的插入操作有两种:在表头插入和在表尾插入。Linux为此提供了两个接口:
所有链表(包括添加、删除、移动和拼接等)操作都是针对数据结构list_head进行的。提供给用户的的添加链表的操作有两种:表头添加和表尾添加。注意到,Linux双循环链表中有一个链表头,表头添加是指添加到链表头之后,而表尾添加则是添加到链表头的prev所指链表节点之后。
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);
因为Linux链表是循环表,且表头的next、prev分别指向链表中的第一个和最末一个节点,所以,list_add和list_add_tail的区别并不大,实际上,Linux分别用
__list_add(new, head, head->next);
和
__list_add(new, head->prev, head);
来实现两个接口,可见,在表头插入是插入在head之后,而在表尾插入是插入在head->prev之后。
static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
普通的在两个非空结点中插入一个结点,注意new、prev、next都不能是空值。