为什么我们需要list_for_each_safe()来删除内核链表中的节点?

时间:2012-02-09 08:58:45

标签: c foreach linux-kernel linked-list kernel

我正在学习如何使用list.h中的内核链接列表API。

我了解到,在使用list_for_each_safe()而不是使用list_del()删除节点时,我需要使用list_for_each()

list_for_each_safe()的代码:

#define list_for_each_safe(pos, n, head) \
    for (pos = (head)->next, n = pos->next; pos != (head); \
        pos = n, n = pos->next)

list_for_each()的代码:

    for (pos = (head)->next; pos != (head); pos = pos->next)

我注意到它们都非常相似,只是_safe版本需要额外的参数作为“临时存储”(在此声明为list.h)。

我知道何时应用相应的功能,_safe版本用于删除,正常版本用于访问,但我很好奇额外的参数如何使其“安全”?

考虑以下内容,我使用list_for_each_safe()删除链接列表中的每个节点:

struct kool_list{
    int to;
    struct list_head list;
    int from;
    };

struct kool_list *tmp;
struct list_head *pos, *q;
struct kool_list mylist;

list_for_each_safe(pos, q, &mylist.list){
         tmp= list_entry(pos, struct kool_list, list);
         printf("freeing item to= %d from= %d\n", tmp->to, tmp->from);
         list_del(pos);
         free(tmp);
    }

如何让q帮助删除?

感谢您的帮助!

2 个答案:

答案 0 :(得分:22)

这是必要的,因为list_del会在内部修改pos字段的值。在您的示例中,循环体甚至可以释放pos占用的内存。假设您将使用不安全版本的循环:

for (pos = (head)->next; pos != (head); pos = pos->next)

执行循环体后pos指针变为无效,破坏增量表达式:pos = pos->next

相反,安全foreach预先将pos->next的值保存在临时变量中,然后引用后者而不是解除引用pos

for (pos = (head)->next, n = pos->next; pos != (head); \
    pos = n, n = pos->next)

答案 1 :(得分:2)

pos = start;
del(pos);
pos = pos->next;

而不是

pos = start;
n = pos->next;
del(pos);
pos = n;

如果del()是free()和memset(),pos-> next是未定义的