前言
Linux内核实现了一下常用的内建数据结构,主要有:
- 链表
- 队列
- 映射
- 二叉树
今天详细学习一下链表的知识,链表是一种存放和操作可变数量元素(常称为节点)的数据结构。Linux内核的标准链表就是采用环形双向链表形式实现的。
链表数据结构
传统的链表是将数据存放在链表节点中;而Linux内核的方式与众不同,它的链表节点只有两个指针(prev和next),链表节点保存在用户数据结构中。
链表代码在头文件< linux/list.h>中声明,数据结构很简单:
struct list_head
{
struct list_head *next;
struct list_head *prev;
}
next指针指向下一个链表节点,prev指向上一个链表节点。然后这个链表节点list_head一般保存在数据的结构体内:
struct fox
{
unsigned long tail_length; //尾巴长度
unsigned long weight; //重量
bool is_fantastic; //狐狸是否奇妙?
struct list_head list; //链表节点存放在此处
}
这样在以后对链表的操作都是针对链表节点list_head进行的,然后根据list_head就可以找到其所在的数据结构,这是通过list_entry()函数实现的:
list_entry(ptr,type,member);
/*
ptr是指向list_head类型的链表的指针
type是数据的结构体,struct fox
member是数据结构体中的一个域,类型为list_head
函数的作用就是根据结构的成员指针找到其所在结构体的指针。
*/
声明和初始化一个链表
Linux提供了两种方式初始化链表。
一种是使用LIST_HEAD()这个宏:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
LIST_HEAD(fox_list);就是定义并初始化了名为fox_list的链表,链表头为fox_list,其next、prev指针都指向自己。
另一种是先定义list_head指针变量,然后用INIT_LIST_HEAD()将其初始化为链表:
struct fox *red_fox;
red_fox=kmalloc(sizeof(*red_fox),GFP_LERNEL);
red_fox->tail_length=40;
red_fox->weigth=6;
red_fox->is_fantastic=false;
INIT_LIST_HEAD(&red_fox->list);//注意参数为指针
添加和删除节点
向链表中添加一个节点:
list_add(struct list_head *new, struct list_head *head);
例:
list_head(&fox->list, &fox_list);
//fox是新建的数据结构体,fox_list是之前初始化的链表
把节点增加到链表尾:
list_add_tail(struct list_head *new, struct list_head *head);
从链表中删除一个节点:
list_del(struct list_head *list);
例:
list_del(&fox->list);
注意,该删除操作并不会释放list或包含list的结构体所长用的内存。仅仅是将其从链表中移除。
从链表中删除一个节点并对其初始化:
list_del_init(struct list_head *list);
把节点从一个链表移动到另一个链表:
list_move(struct list_head *list, struct list_head *head);
//把list项从链表中移除,并添加到另一个链表的head节点后面
把节点从一个链表移动到另一个链表的末尾:
list_move_tail(struct list_head *list, struct list_head *head);
检查链表是否为空:
list_empty(struct list_head *head);
若链表为空返回非0值,否则返回0。
合并两个链表:
list_splice(struct list_head *list, struct list_head *head);
list_splice_init(struct list_head *list, struct list_head *head);
//合并后初始化原来的链表
将list指向的链表插入到指定的链表head节点后面。
遍历链表
最简单的方法就是使用list_for_each()宏遍历链表,再通过list_entry()获取完整数据结构体
例:
struct list_head *p;
struct fox *f;
list_for_each(p, &fox_list)
{
f=list_entry(p, struct fox, list);
}
以上两个函数可以合并为一个:list_for_each_entry()
struct fox *f;
list_for_each_entry(f, &fox_list, list)
{
//f就遍历了所有的数据结构体;
}
反向遍历链表:
list_for_each_entry_reverse(pos, head, member);
遍历的同时安全删除节点:
list_for_each_entry_safe(pos, next, head, member);