【问题标题】:Explain list_for_each_entry and list_for_each_entry_safe解释 list_for_each_entry 和 list_for_each_entry_safe
【发布时间】:2022-02-27 15:49:23
【问题描述】:

谁能解释list_for_each_entry 和...entry_safe 循环在linux 中的工作原理。 就像

list_for_each_entry(type *cursor, struct list_head *list, member)

list_for_each_entry_safe(type *cursor, type *next, struct list_head *list,member)

所有这些参数的作用是什么,以及它们是如何用来遍历列表的。

提前感谢

【问题讨论】:

  • 我建议你阅读了解Linux内核的书。
  • 感谢您的建议,本书与list相关的内容不错..

标签: list linux-kernel linux-device-driver


【解决方案1】:

编辑:抱歉,肯定来晚了,我打错了很多字。

它们很有趣! :) 不同之处在于,如果您在迭代列表时删除某些内容,list_for_each_entry 会中断,而list_for_each_entry_safe 不会(当然,以一些额外的 CPU 指令为代价)。

尽管在 list.h 中有一个很好的链表实现,但内核已经确定了双向链表(我假设你理解)。您的清单只是:

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

请注意,相同的结构用于列表的“头”以及每个节点。当列表为空时,头部的nextprev 成员只是指向头部自身。因此,迭代列表只是从头的next 成员开始并调用该节点的过程,除非它与prev 的地址相同(当您停止时)。否则,您的 for 主体将被调用,您可以使用 container_of() 宏获取指向实际结构的指针并使用它。然后,在for 的第三个字段中,我们只是移动到下一个next

编辑:哎呀,对不起,您要求对参数进行解释。好吧,如果我是你,我会直接检查它,而不是听信别人的话。对于那些,我建议他们自己使用Kernel API docs,它们至少存在于链表库中。我正在尝试获取一个补丁集,该补丁集也会将它们添加到红黑树库中,但是通过它可能是一个相当大的过程。

另请注意:http://kernelnewbies.org/FAQ/LinkedLists

这是一个简单的例子:

struct list_head my_actual_list;
struct my_struct {
    struct list_head node;
    /* some other members */
};

/* in a function body somewhere... */
struct list_head *i;
list_for_each(i, &my_actual_list) {
    struct my_struct *obj = list_entry(i, struct my_struct, node);
    // do something with obj
}

list_entry 只是container_of 的别名

编辑#2

好的,所以在回答您在 cmets 中的问题时,我将扩展我的答案。我实际上可以理解理解这个概念的困难,因为与 C++ STL 容器、C 数组等相比,它确实有一些奇怪的东西,但是一旦你习惯了这些习语,它就会显得很自然。还是在未来,我真的强烈建议您开始自己查看这些结构、函数和宏的定义,并尝试拼凑出一个理解,然后提出问题。

首先,列表中的每个节点都是一个包含struct list_head 类型成员的结构,并且列表其自身struct list_head 类型。因此,在这种情况下,谁是容器,谁是被包含物,仅取决于它们的使用方式,但通常会以这些成员的名称来表达。迭代器的类型是struct list_head *。这是一个示例,我将用它们的等效代码替换正常的函数和宏调用:

struct my_container {
    struct list_head list;
    int some_member;
    /* etc. */
};

struct my_obj {
    struct list_head node;
    int some_member;
    /* etc. */
};

void func() {
    struct my_container container;
    struct my_obj obj1, obj2;
    struct list_head *i;

    /* INIT_LIST_HEAD(&container.list); */
    container.list.next = &container.list;
    container.list.prev = &container.list;

    /* list_add_tail(&obj1.node); */
    container.list.prev = &obj1.node;
    obj1.node.next = &container.list;
    obj1.node.prev = &container.list;
    container.list.next = &obj1.node;

    /* list_add_tail(&obj2.node); */
    container.list.prev = &obj2.node;
    obj2.node.next = &container.list;
    obj2.node.prev = &obj1.node;
    obj1.node.next = &obj2.node;

    /* list_for_each(i, &container.list) { */
    for (i = container.list.next; i != &container.list; i = i->next) {
        struct my_obj *obj = list_entry(i, struct my_obj, node);
        /* do stuff */
    }

}

Now go read! :)

【讨论】:

  • 我得到你了,但我想知道它们是如何相关的以及这个迭代器将检查什么条件。例如,如果我们使用 for 循环,那么 for(int i= 0;i 像这样这个迭代器接下来将如何迭代,它将在什么基础上检查它的下一个元素..
  • 啊,是的,你会注意到我使用了变量名i,因为我称它为我的迭代器。有些人使用变量名pos,因为它是列表中的“位置”,但我更喜欢坚持“迭代器”约定。这很长,所以我将扩展我的答案。
猜你喜欢
  • 2021-12-14
  • 1970-01-01
  • 2021-01-05
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多