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

I'm*_*gon 12 c foreach kernel linked-list linux-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)
Run Code Online (Sandbox Code Playgroud)

代码list_for_each():

    for (pos = (head)->next; pos != (head); pos = pos->next)
Run Code Online (Sandbox Code Playgroud)

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

我知道什么时候应用函数correcly,_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);
    }
Run Code Online (Sandbox Code Playgroud)

如何q帮助删除?

谢谢你的帮助!

Eld*_*mov 22

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

for (pos = (head)->next; pos != (head); pos = pos->next)
Run Code Online (Sandbox Code Playgroud)

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

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

for (pos = (head)->next, n = pos->next; pos != (head); \
    pos = n, n = pos->next)
Run Code Online (Sandbox Code Playgroud)