List for each entry

本文深入探讨了链表遍历与初始化的核心技术,包括链表节点定义、链表遍历函数list_for_each_entry的实现原理及应用场景,以及初始化函数LIST_HEAD_INIT和INIT_LIST_HEAD的区别与使用场景。通过具体代码示例,详细解析了如何高效地遍历和初始化双向链表,旨在提升开发者在链表操作上的实践能力。

List for each entry

#define list_entry(link, type, member) \
	((type *)((char *)(link)-(unsigned long)(&((type *)0)->member)))

#define list_head(list, type, member)		\
	list_entry((list)->next, type, member)

#define list_tail(list, type, member)		\
	list_entry((list)->prev, type, member)

#define list_next(elm, member)					\
	list_entry((elm)->member.next, typeof(*elm), member)

#define list_for_each_entry(pos, list, member)			\
	for (pos = list_head(list, typeof(*pos), member);	\
	     &pos->member != (list);				\
	     pos = list_next(pos, member))

member是pos所属类型的一个成员变量;

list为一系列member所链接成的一个双向链表, 但同时list中所有的member均存在着对应的pos类型的实体(假设为pos_A, pos_B, pos_C, ...)。

list_for_each_entry(pos, list, member)的功能就是遍历list,从中取出member变量,再利用 list_entry找到此 member变量所对应的pos类型的实体(pos_A, pos_B, pos_C, ...)赋值给pos,直到完成for循环。

  • 应用场景

在实际使用时,这个member可以用list_head:

struct list_head {
	struct list_head *next, *prev;
};

然后不论是何种数据结构,只要往其中加入list_head成员,则可以方便地使用list_for_each_entry进行链表的遍历了,避免了在链表操作上花费更多的时间。


  • LIST_HEAD_INIT和INIT_LIST_HEAD有什么区别?

首先,看定义:

#define LIST_HEAD_INIT(name) { &(name), &(name) }

static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

功能上应该是相同的,都是将name/list的next、prev指针指向它本身,即对链表进行初始化。

那为什么要定义两个呢?

其实这两个宏是用于不同的情况的:

LIST_HEAD_INIT用于定义变量时初始化,如:

struct list_head name = LIST_HEAD_INIT(name);

而list_head变量已经存在,那如果还使用上述宏则会报错:

error: expected ';' before '}' token

原因是C/C++中,类似于{}对结构体的赋值只有在定义变量时才能使用,这时就要使用 INIT_LIST_HEAD:

LIST_HEAD_INIT(&m_fileList);

——尤其是在C++的class中,只能使用后者,因为class中是不能对非静态成员变量进行初始化的。

### 关于 `list_for_each_entry_safe` 的用法与解释 `list_for_each_entry_safe` 是 Linux 内核中用于安全遍历链表的一个宏。它的主要特点是能够在遍历过程中安全地删除当前节点或其他节点,而不会导致遍历中断或崩溃。这是因为该宏会在每次迭代前预先把下一个节点存储起来[^2]。 以下是 `list_for_each_entry_safe` 的基本语法及其参数说明: #### 基本语法 ```c list_for_each_entry_safe(pos, n, head, member) { // 对 pos 节点执行操作 } ``` - **pos**: 当前正在访问的节点指针。 - **n**: 下一个节点指针,在每次迭代之前被设置为 `pos->member.next`,从而确保即使删除了当前节点,仍然能够继续遍历链表。 - **head**: 表示链表头的地址。 - **member**: 结构体内嵌套的 `struct list_head` 类型成员变量名称。 #### 使用场景 当需要在遍历链表的同时动态修改链表结构(例如删除某些满足条件的节点),推荐使用 `list_for_each_entry_safe`。相比普通的 `list_for_each_entry`,后者无法处理在遍历时发生的链表结构调整问题[^2]。 --- ### 示例代码 下面是一个典型的例子,展示如何利用 `list_for_each_entry_safe` 删除符合条件的链表节点: ```c #include <linux/list.h> #include <linux/kernel.h> // 定义链表节点结构体 struct my_node { int data; struct list_head list; }; void delete_nodes(struct list_head *head, int threshold) { struct my_node *pos, *n; // 遍历并安全删除数据小于阈值的节点 list_for_each_entry_safe(pos, n, head, list) { if (pos->data < threshold) { printk(KERN_INFO "Deleting node with data: %d\n", pos->data); list_del(&pos->list); // 从链表中移除节点 kfree(pos); // 释放内存资源 } } } int main() { struct list_head head; INIT_LIST_HEAD(&head); // 创建一些测试节点... struct my_node nodes[] = {{1}, {2}, {3}}; for (size_t i = 0; i < sizeof(nodes)/sizeof(*nodes); ++i) { INIT_LIST_HEAD(&nodes[i].list); list_add_tail(&nodes[i].list, &head); } // 执行删除逻辑 delete_nodes(&head, 3); return 0; } ``` 在这个例子中,我们创建了一个简单的双向链表,并通过调用 `delete_nodes()` 方法删除所有数据字段小于指定阈值的节点。注意这里的关键在于使用了 `list_for_each_entry_safe` 来保证删除的安全性[^5]。 --- ### 注意事项 1. **链表初始化** 在使用任何链表操作宏之前,请务必确认链表已经正确初始化(通常通过 `INIT_LIST_HEAD()` 实现)。未初始化的链表可能导致不可预测的行为[^4]。 2. **多线程环境下的同步机制** 如果链表处于并发访问环境中,则应考虑引入适当的同步原语(如自旋锁 `spin_lock` 或互斥量 `mutex`)以保护共享数据的一致性和完整性。 3. **性能权衡** 尽管 `list_for_each_entry_safe` 提供了安全性保障,但它可能会稍微降低效率,因为需要额外维护临时变量 `n` 存储下一项位置的信息。如果无需修改链表结构,建议改用更高效的 `list_for_each_entry`[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值