linux内核中list的基本用法

内核链表

1 list_head 结构

为了使用链表机制,驱动程序需要包含<linux/types.h>头文件,该文件定义了如下结构体实现双向链:

c 复制代码
struct list_head {
	struct list_head *next, *prev;
};

2 链表的初始化

2.1 链表宏定义和初始化

可使用以下宏定义并初始化一个链表头部list_head,list_head 不包含数据部分。LIST_HEAD_INIT将链表头的 next 和 prev 指针都指向链表头部,从而形成一个循环结构,和下面介绍的INIT_LIST_HEAD函数一样。

c 复制代码
#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)
2.2 链表的初始化

INIT_LIST_HEAD 是一个用于初始化链表头的函数,它将链表头的 next 和 prev 指针都指向自己,从而形成一个循环结构。

c 复制代码
static inline void INIT_LIST_HEAD(struct list_head *list)
{
	WRITE_ONCE(list->next, list);
	WRITE_ONCE(list->prev, list);
}

如下图所示,链表头的 next 和 prev 指针都指向自己。

3 list_add

在链表的头部添加新链表项,以下是实现:

c 复制代码
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}


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);
}

以下为添加示意图,可以看出后添加节点放在链表的头部,先添加节点靠后,先进后出,后进先出,类似栈结构。

4 list_add_tail

在链表的尾部添加新链表项,以下是实现:

c 复制代码
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

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);
}

以下为添加示意图,可以看出新添加节点放在链表的尾部,后添加节点靠,先进先出,后进后出,类似FIFO结构。

5 遍历节点

5.1 list_entry

list_entry 宏通过调用 container_of 宏,从链表节点指针获取包含该节点的结构体指针。

c 复制代码
/**
 * list_entry - get the struct for this entry
 * @ptr: 指向 &struct list_head 的指针。
 * @type: 包含该节点的结构体类型。
 * @member: 结构体中的 list_struct 名称。
 */
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)
5.2 list_for_each

list_for_each 从链表的头部往后依次遍历(next方向)。

c 复制代码
/**
 * list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 */
#define list_for_each(pos, head) \
	for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next)
5.3 list_for_each_entry

通过for循环,依次遍历链表中的每个节点,next方向遍历,每个节点的宿主为pos。

c 复制代码
/**
 * list_for_each_entry	-	iterate over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 */
#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))

a. list_first_entry 宏:

c 复制代码
#define list_first_entry(ptr, type, member) \
    container_of((ptr)->next, type, member)

list_first_entry 宏用于获取链表的第一个节点的结构体指针。通过 (ptr)->next 获取到链表头部之后的第一个节点的指针,然后通过 container_of 宏获取包含该节点的整个结构体指针。

b. list_entry_is_head 宏:

c 复制代码
#define list_entry_is_head(pos, head, member) \
    ((pos)->member == (head))

list_entry_is_head 宏用于检查当前节点是否是链表的头部。比较 pos->member 是否等于 head,如果相等,则说明当前节点是链表的头部,即遍历结束。

c. list_next_entry 宏:

c 复制代码
#define list_next_entry(pos, member) \
    list_entry((pos)->member.next, typeof(*(pos)), member)

list_next_entry 宏用于获取下一个节点的结构体指针。通过 (pos)->member.next 获取到当前节点的下一个节点的指针,然后通过 list_entry 宏获取包含该节点的整个结构体指针。

5.4 list_for_each_prev

通过for循环,依次遍历链表中的每个节点,与list_for_each_entry不同的是list_for_each_prev按照pre方向遍历,每个节点的宿主为pos。

c 复制代码
/**
 * list_for_each_prev	-	iterate over a list backwards
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 */
#define list_for_each_prev(pos, head) \
	for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev)
5.5 删除链表

list_del

删除列表中的给定项

c 复制代码
/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty() on entry does not return true after this, the entry is
 * in an undefined state.
 */
static inline void list_del(struct list_head *entry)
{
	__list_del_entry(entry);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}

list_del_init

删除列表中的给定项,如果删除后的链表可能被插入新的链表中,应该使用list_del_init,它会初始化链表的指针。

c 复制代码
/**
 * list_del_init - deletes entry from list and reinitialize it.
 * @entry: the element to delete from the list.
 */
static inline void list_del_init(struct list_head *entry)
{
	__list_del_entry(entry);
	INIT_LIST_HEAD(entry);
}
相关推荐
x-cmd32 分钟前
Mac 涨价后,本地 AI 还能千元入门吗?
linux·人工智能·macos·ai·agent·amd·本地ai入门
潘潘的嵌入式日记2 小时前
改完Keil工程文件被缓存覆盖?——uvprojx编辑的进阶四坑
xml·嵌入式·keil·调试·mdk·工程文件
ICECREAM6 小时前
嵌入式 —— 串口与 UART 深入详解 —— i.MX6ULL 实战
嵌入式
摇滚侠7 小时前
Linux 零基础教程 09、11、77
linux·运维·服务器
嵌入式学习和实践9 小时前
嵌入式 Linux 调闭源 SDK 怕崩?用 dlopen 把第三方动态库库“隔离“起来
linux·三方库
小樱花的樱花10 小时前
Linux 多线程编程:互斥锁(Mutex)详解
linux·c语言·开发语言
麦兜和小可的舅舅10 小时前
从原理到实战:Linux 系统性能诊断核心指标全解析及生产系统故障分析复盘
大数据·linux·运维
浅止菌10 小时前
嵌入式Linux内存优化:从OOM到稳定运行的6个实战技巧
嵌入式
大辉狼_音频架构10 小时前
Vol.05 VendorHAL-NXP
嵌入式·音视频开发
mounter62511 小时前
质检员与超能引擎的碰撞:KASAN 护航 eBPF JIT 的技术演进与安全抉择
linux·ebpf·linux kernel·kernel·kasan