二: 内核链表的实现接口
1. 链表的初始化
链表的初始化并没有建立链表的头部,而是将struct list_head结构体中的两个指针指向自己。例如我们在初始化一个名为node的链表时,我们可以采用下面两种方式进行初始化:
1.1、静态初始化:静态初始化就是在声明的同时初始化链表。
初始化的方法为: LIST_HEAD(node) ;
在内核中LIST_HEAD()是一个宏定义,内核中的实现方式是:
#define LIST_HEAD_INIT(name) {&(name),&(name)}
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name);
1.2、 动态初始化:在初始化前,需要定义一个链表的结构体。
初始化的方法为: struct list_head node;
INIT_LIST_HEAD(&node);
在内核中INIT_LIST_HEAD()也是一个宏定义,内核中的实现方式为:
#define INIT_LIST_HEAD(ptr) do { /
(ptr)->next = (ptr);/
(ptr)->prev = (ptr);/
}while(0);
2. 插入、删除、合并接口
2.1、内核链表的插入可以分为插入到链表的首部和插入到链表的尾部。分别通过函数:
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内核链表是一种循环链表,所以加入到链表的首部或者是链表的尾部都是一样的,两者调用的函数都是
__list_add(),只是调用的时候的实参不一样而已。__list_add()的实现原理如下:
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;
};
2.2、内核链表的删除提供了两个函数接口,分别为:
static inline void list_del(struct list_head *entry);
static inline void list_del_init(struct list_head *entry);
这两个函数的目的相同,在实现方式上都是调用__list_del();不同的是,前者删除节点后,并将删除后的节点的两个指针
赋值为LIST_POSITION1和LIST_POSITION2,后者是将节点删除后,并肩删除后的节点的指针指向删除的节点本身
(即调用了LIST_INIT_HEAD())。
2.3、移除:是指将节点从一个链表删除并将该节点加入到另外的一个链表中。
移除的实现原理就是删除和添加的一个结合体,所以,在添加到另外的一个链表时,可以添加到首部也可以使尾部
所以,他的实现函数有两个,分别是:
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);
2.4、合并:是将两个链表合并为一个链表。合并的实现函数为:
static inline void list_splice(struct list_head *list, struct list_head *head);
static inline void list_splice_init(struct list_head *list, struct list_head *head);
它的实现原理图如下:
链表合并list_splice(&list1,&list2)
3、链表的遍历
链表的遍历就是将从头开始访问链表中的每一个节点,直到起始节点为止。链表的遍历有下一个依次遍历也可以从前一个
依次遍历,遍历的目的是为了找出链表中每个节点中的数据域内容,所以,我们在遍历的时候需要另外一个能提取数据域的
函数list_entry()。为了保证遍历时的安全性,我们可以在遍历中加入判断,判断当前的节点指针是不是一个空值,或者
采用函数,list_empty( )判断是不是结束。
遍历的函数: list_for_each(pos, head); //正常的遍历
list_for_each_prev(pos, head) //反向遍历
第一个参数为一个struct list_head *的指针,他的值随着遍历指向链表中的各个节点。
第二个参数为我们初始化的一个链表头部。
获取数据的函数: list_entry(ptr, type, member);
第一个参数为一个struct list_head *的指针,它的位置是读取链表中哪个位置的数据。
第二个参数是带有该链表的结构体的类型。
第三个参数是该结构体中链表类型的变量。
例如:我们在遍历一个初始化一个一个链表lst,并将几个struct node元素加入到链表后,开始遍历该链表。
struct list_head *p_list;
list_for_each(p_list,&lst)
{
struct node *cur = list_entry(p_list,struct node,list);
if(cur == NULL)
break;
printk("data = %s/n",cur->data);
}
附:链表的一个例子