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 的地址将用于确定迭代是否到达列表的末尾(或者实际上是再次到达列表对象)。
这也是为什么空列表被定义为next 和prev 指向struct list_head 本身的原因。
我也需要一些时间来解决这个问题。 ;)