一、链表介绍
链表属于线性表的一种,是线性表的链式存储,即逻辑上相邻的两个元素在物理存储上并不连续,而是通过指针链接起来的节点来存储数据元素。
1、单链表
1、单向链表
单向链表就一个指针域next,最后一个结点的指针域为NULL,也是遍历单向链表的结束条件。
2、单向循环链表
单链循环表就是将最后一个结点的指针域指向头结点,此时遍历结点的结束条件为是否是头结点的地址。如下图:
2、双链表
1、双向链表
双链表就是有两个指针域,一个指向前驱结点的地址,一个指向后继结点的地址,很明显双向链表要比单链表更容易找到前驱结点。所以双链表用的更多一点。
2、双向循环链表
双向循环链表就是把第一个结点的前驱指针域由NULL改为最后一个结点的地址,而最后一个结点的后继指针域由NULL改为第一个结点的地址,由此形成了循环。在Linux内核中实现的就是双向循环链表。
二、Linux内核链表
Linux内核链表的实现是放在list.h文件中,而且实现了两种链表,一种是双向循环链表,另一种是哈希链表;并且内核链表实现不绑定任何的结构,目的是为了实现双向链表的通用操作,可以和任意数据类型结合形成链表结点,以下描述的是双向循环链表的实现。
1、内核双向循环链表实现源码(5.4.19)
-----------------------------------------------------------------------------------------------
/* 初始化头节点head(静态编译方法) */
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
/* 初始化头节点head(动态运行方法) */
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
-----------------------------------------------------------------------------------------------
/* 向链表添加节点(__开头为内核内部实现函数) */
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
/* 在头节点后面插入新的节点接口(顺序遍历的时候数据是逆向输出) */
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/* 在尾部插入新的节点接口 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
---------------------------------------------------------------------------------------------
/* 删除节点实现函数 */
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
WRITE_ONCE(prev->next, next);
}
static inline void __list_del_entry(struct list_head *entry)
{
if (!__list_del_entry_valid(entry))
return;
__list_del(entry->prev, entry->next);
}
/* 删除链表节点接口 */
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
----------------------------------------------------------------------------------------------
/* 判断链表是否为空接口,仅判断一个节点和头相等 */
static inline int list_empty(const struct list_head *head)
{
return READ_ONCE(head->next) == head;
}
/* 判断链表是否为空接口,前后节点都和头相等,(这主要是为了应付另一个cpu正在处理同一个链表而造成next、prev不一致的情况) */
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = smp_load_acquire(&head->next);
return (next == head) && (next == head->prev);
}
-----------------------------------------------------------------------------------------------
/* 遍历链表节点(仅读) */
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
/* 遍历链表节点(可操作节点)*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
-----------------------------------------------------------------------------------------------
/* 获取结构体的首地址然后遍历每一个节点(遍历节点,删除节点会出错,原因是pos->next节点依赖pos节点) */
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/* 上面函数的安全版本(若遍历到节点,然后删除节点,释放空间用这个) */
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
-----------------------------------------------------------------------------------------------
/* 从成员(这里指list成员)的地址获取结构体的首地址 */
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
!__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
/* 获取第一个节点(不是头节点,头节点不含数据)结构体的首地址 */
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/* 获取最后一个节点结构体的首地址 */
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
在实际使用过程中,链表操作有,初始化头节点(LIST_HEAD/INIT_LIST_HEAD),向链表中添加节点(list_add/list_add_tail),删除链表节点(list_del),判空操作(list_empty/list_empty_careful),遍历节点,实际上是遍历节点中的数据域(list_for_each_entry/list_for_each_entry_safe)
2、实际应用
#include<linux/module.h>
2 #include<linux/list.h>
3 #include<linux/errno.h>
4 #include<linux/slab.h>
5
6 struct student {
7 unsigned int num;
8 unsigned int age;
9 struct list_head node;
10 };
11
12 struct list_head g_student_list;
13
14 static int student_add(unsigned int num, unsigned int age, unsigned int flag)
15 {
16 struct student *str = NULL;
17
18 str = kmalloc(sizeof(struct student), GFP_KERNEL);
19 if (str == NULL) {
20 printk("malloc memory failed\n");
21 return -ENOMEM;
22 }
23
24 str->num = num;
25 str->age = age;
26 INIT_LIST_HEAD(&(str->node));
27
28 if (flag == 0)
29 list_add(&(str->node), &g_student_list);
30 else
31 list_add_tail(&(str->node), &g_student_list);
32
33 return 0;
34 }
static int student_del(unsigned int num, unsigned int age)
38 {
39 struct student *p = NULL;
40 struct student *n = NULL;
41
42 if (!list_empty(&g_student_list)) {
/* 用list_for_each_entry遍历删除会报段错误 */
43 list_for_each_entry_safe(p, n, &g_student_list, node) {
44 if ((num == p->num) && (age == p->age)) {
45 list_del(&(p->node));
46 kfree(p);
47 }
48 }
49 }
50
51 return 0;
52 }
53
54 static void destroy_list(void)
55 {
56 struct student *p = NULL;
57 struct student *n = NULL;
58
59 if (!list_empty(&g_student_list)) {
60 list_for_each_entry_safe(p, n, &g_student_list, node) {
61 list_del(&(p->node));
62 kfree(p);
63 }
64 }
65
66 return;
67 }
static void student_print(void)
70 {
71 struct student *p = NULL;
72
73 /**
74 * 参数1. struct student 结构体指针, 用来保存从链表中各节点中获得首地址
75 * 参数2. 链表
76 * 参数3. 结构的中的成员变量node(struct list_head类型)的名字
77 */
78 if (!list_empty(&g_student_list)) {
79 list_for_each_entry(p, &g_student_list, node) {
80 printk("num=%u, age=%u\n", p->num, p->age);
81 }
82 }
83
84 return;
85 }
static int __init list_init(void)
88 {
89
90 printk("module init\n");
91 INIT_LIST_HEAD(&g_student_list);
92
93 student_add(1, 20, 0);
94 student_add(2, 21, 0);
95 student_add(3, 21, 0);
96 student_add(4, 22, 0);
97 student_add(5, 23, 0);
98 student_add(6, 23, 1);
99 /* 先打印全部遍历节点 */
100 student_print();
101
102 student_del(6, 23);
103 /* 删除6,23节点后,在遍历节点*/
104 student_print();
105
106 return 0;
107 }
108
109 static void __exit list_exit(void)
110 {
111 printk("module exit\n");
112 destroy_list();
113 return;
114 }
115
116
117 module_init(list_init);
118 module_exit(list_exit);
119 MODULE_LICENSE("GPL")
insmod list.ko,dmesg查看信息: