linux内核之双向循环链表

一、链表介绍

链表属于线性表的一种,是线性表的链式存储,即逻辑上相邻的两个元素在物理存储上并不连续,而是通过指针链接起来的节点来存储数据元素。

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查看信息:
在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值