目录
list_for_each_entry_safe 是 Linux 内核中用于遍历链表的一个宏,特别适用于在遍历过程中可能需要删除当前节点的情况。它通过使用两个指针来确保即使在删除当前节点时,遍历过程也不会被打断。
宏定义
c
深色版本
#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); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
- 参数:
pos: 当前正在访问的链表元素。n: 下一个链表元素,用于防止在删除当前节点时导致遍历中断。head: 链表头指针。member: 结构体中链表节点的名字。
工作原理
list_for_each_entry_safe 主要解决了在遍历过程中删除节点可能导致的问题。例如,如果你直接使用 list_for_each_entry 来遍历链表,并且在遍历过程中删除了当前节点,那么下一次循环时由于当前节点已经被移除,可能会导致未定义行为或崩溃。
为了避免这种情况,list_for_each_entry_safe 使用了一个额外的指针 n 来保存下一个节点的位置,这样即使当前节点被删除,我们仍然知道下一个要访问的节点是谁。
示例代码
假设你有一个结构体和一个链表:
c
深色版本
struct my_struct {
int data;
struct list_head list; // 嵌入到结构体中的链表节点
};
LIST_HEAD(my_list); // 初始化一个空链表头
现在,让我们看一个如何使用 list_for_each_entry_safe 的例子,其中我们将遍历链表并删除所有 data 等于某个特定值的节点:
c
深色版本
void remove_entries(struct list_head *head, int value_to_remove)
{
struct my_struct *pos, *tmp;
list_for_each_entry_safe(pos, tmp, head, list) {
if (pos->data == value_to_remove) {
list_del(&pos->list); // 删除当前节点
kfree(pos); // 释放内存(如果适用)
}
}
}
在这个例子中:
pos是当前遍历到的链表元素。tmp是指向下一个链表元素的指针,这样即使我们删除了pos,仍然可以安全地继续遍历。- 如果
pos->data等于我们要删除的值,我们就调用list_del()删除该节点,并调用kfree()释放内存(如果该结构体是动态分配的)。
注意事项
- 类型推导:
typeof(*pos)自动推导出pos指向的结构体类型。这使得代码更加简洁,但需要注意确保pos类型正确。 - 成员名:
member参数是指向嵌入在结构体中的struct list_head成员的名字。确保这个名称与结构体定义中的实际成员名称一致。 - 性能: 虽然
list_for_each_entry_safe提供了安全性,但它也引入了一些额外的开销,因为需要维护第二个指针n。因此,在不需要删除节点的情况下,推荐使用更高效的list_for_each_entry。
总结
list_for_each_entry_safe 是一个强大的工具,特别适合那些在遍历过程中可能需要修改链表(如删除节点)的场景。通过预先保存下一个节点的位置,它可以保证遍历过程不会因链表结构的变化而中断。然而,对于仅需读取链表内容而不修改链表结构的场景,使用 list_for_each_entry 可能会更高效。
:list_for_each_entry_safe&spm=1001.2101.3001.5002&articleId=147790992&d=1&t=3&u=8ccc2348b0d64a4097987961ec51a01e)

被折叠的 条评论
为什么被折叠?



