【问题标题】:Adding items to a Linux kernel linked list将项目添加到 Linux 内核链表
【发布时间】:2015-11-25 18:33:56
【问题描述】:

我在我的代码中使用linux/list.h 来实现队列/堆栈行为。头尾添加API如下:

static inline void list_add(struct list_head *new, struct list_head *head)
{
         __list_add(new, head, head->next);
}

list_add_tail 类似。令人惊讶的是,它什么都不返回(void),这是否意味着使用此 API 在内核中添加列表总是成功的。我知道这里没有 full 的概念,但是如果新节点的内存分配不可用以及其他可能的原因怎么办?

【问题讨论】:

标签: c list linux-kernel


【解决方案1】:

list API 不会动态分配任何内存。我自己觉得这件事有点令人费解。这里的问题是 Linux 是用 C 而不是 C++ 编写的,而是以一种非常面向对象的方式实现的,但在 C 中它看起来像从里到外。它的工作原理如下(这也适用于其他几个 Linux API,例如kobj):

您定义了一些struct,它应该是列表的成员。与您通常认为的链表相反,此对象不会通过分配一些不透明的列表项并有一个指向您的实际对象的指针来放入列表中,您将 struct list_head 设为实际的 成员 你的struct

struct something {
    struct list_head list;
    uint8_t some_datum;
    uint16_t some_other_datum;
    void *a_pointer;
};

您的列表将是一些独立 struct list_head:

static LIST_HEAD(list_of_somethings);

要向list_of_somethings 添加元素,您现在需要执行类似的操作

struct something *s = kmalloc(sizeof(*s), GFP_KERNEL);
s->some_datum = 23;
s->some_other_datum = 0xdeadbeef;
s->a_pointer = current;
list_add(&s->list, &list_of_somethings);

换句话说,您已经分配了元素。这看起来很奇怪,但很优雅。这种“设计模式”允许在 C 中使用类型不透明的列表,这在另一种方式中并不容易做到:一个列表本身就是一堆相互指向的struct list_heads。正如您知道哪个实际的 struct 是您作为程序员所期望的那样,您知道这个 struct 的哪个元素是实际的 list_head 并且可以使用 container_of 宏来获取指向您放置的最终 struct 的指针进入列表:

struct list_head *p = &list_of_somethings.next;
struct something *s = container_of(p, struct something, list);
pr_notice("some data = %i\n", s->some_data);

请注意,表示列表本身的实际struct list_head<linux/list.h> 中定义的迭代宏特别处理,即

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

list_of_somethings 的地址将用于确定迭代是否到达列表的末尾(或者实际上是再次到达列表对象)。 这也是为什么空列表被定义为nextprev 指向struct list_head 本身的原因。

我也需要一些时间来解决这个问题。 ;)

【讨论】:

  • 感谢 Andreas 的详细解释。我完全错过了我已经分配了包含 list_head 的节点这一点。无论如何,你的回答给了我更多的信息。
  • 很好的答案,但是为什么要将已经是 &list_of_somethings.nexthead 传递给从 pos=(head)->next 开始的 list_for_each?我们不会错过第一个元素吗?我想正确的做法是将&list_of_somethings 作为head 传递给list_for_each
  • 这只是列表的语义,正如我在最后一段中试图表达的那样:列表的“第一个”元素通常不是列表的一部分,而只是持有列表的唯一 struct list_head (即包含此列表的另一个上级对象的一些 struct list_head-typed 成员)。
【解决方案2】:

看起来所有的内存都已经被你分配了,所以它真正做的就是连接已经分配的指针。

内存已经存在,它所做的只是填充它,所以没有任何真正的方法可以失败。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    • 2018-09-15
    • 2020-02-02
    • 2011-07-11
    • 2016-11-14
    相关资源
    最近更新 更多