将项目添加到 Linux 内核链表
Adding items to a Linux kernel linked list
我在我的代码中使用 linux/list.h
来实现 queue/stack 行为。在head/tail添加的API如下:
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
类似 list_add_tail
。令人惊讶的是,它 returns 什么都没有 (void
),所以这是否意味着使用此 API 添加到内核中的列表总是成功的。我知道这里没有 full 的概念,但是如果新节点的内存分配不可用以及其他可能的原因怎么办?
看起来所有的内存都已经被你分配了,所以它真正做的就是连接已经分配的指针。
内存已经存在,它所做的只是填充它,所以没有任何真正的方法可以失败。
list
API 不动态分配任何内存。我发现这件事有点令人费解,我自己。这里的问题是 Linux 是用 C 而不是 C++ 编写的,但是以一种非常面向对象的方式实现了一些东西,但是在 C 中它看起来像是由内而外。它的工作原理如下(这也适用于其他几个 Linux APIs,例如 kobj
):
你定义了一些struct
,它应该是一个列表的成员。与你通常认为的链表相反,这个对象不会通过分配一些不透明的列表项并有一个指向你的实际对象的指针来放入列表中,你使 struct list_head
成为一个实际的 [=您的 struct
的 50=] 成员:
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);
换句话说,您已经分配了元素。这看起来很奇怪,但是他妈的很优雅。这个 "design-pattern" 允许在 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
本身的原因。
我也需要一些时间来解决这个问题。 ;)
我在我的代码中使用 linux/list.h
来实现 queue/stack 行为。在head/tail添加的API如下:
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
类似 list_add_tail
。令人惊讶的是,它 returns 什么都没有 (void
),所以这是否意味着使用此 API 添加到内核中的列表总是成功的。我知道这里没有 full 的概念,但是如果新节点的内存分配不可用以及其他可能的原因怎么办?
看起来所有的内存都已经被你分配了,所以它真正做的就是连接已经分配的指针。
内存已经存在,它所做的只是填充它,所以没有任何真正的方法可以失败。
list
API 不动态分配任何内存。我发现这件事有点令人费解,我自己。这里的问题是 Linux 是用 C 而不是 C++ 编写的,但是以一种非常面向对象的方式实现了一些东西,但是在 C 中它看起来像是由内而外。它的工作原理如下(这也适用于其他几个 Linux APIs,例如 kobj
):
你定义了一些struct
,它应该是一个列表的成员。与你通常认为的链表相反,这个对象不会通过分配一些不透明的列表项并有一个指向你的实际对象的指针来放入列表中,你使 struct list_head
成为一个实际的 [=您的 struct
的 50=] 成员:
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);
换句话说,您已经分配了元素。这看起来很奇怪,但是他妈的很优雅。这个 "design-pattern" 允许在 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
本身的原因。
我也需要一些时间来解决这个问题。 ;)