我在代码中使用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在内核中添加列表总是成功的。我知道这里没有完整的概念,但是如果新节点的内存分配不可用以及其他可能的原因怎么办?
答案 0 :(得分:8)
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);
换句话说,您已经分配了元素。这看起来很奇怪,但是像f * ck一样优雅。这种“设计模式”允许在C中使用类型不透明的列表,这在另一种方式下并不容易:单独的列表只是一堆struct list_head
指向彼此。如您所知,作为程序员的实际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
本身的原因。
我也需要一些时间来解决这个问题。 ;)
答案 1 :(得分:1)
看起来你当时已经分配了所有的内存,所以它真正做的就是连接已经分配的指针。
内存已经存在,所有它正在填充它,所以没有任何真正的方法可以失败。