c之关于linux中container_of宏的疑问

熊孩纸 阅读:39 2025-02-15 21:57:57 评论:0

为什么要使用 container_of 宏?

container_of(pointer, container_type, container_field); 

LDD中说

"This macro takes a pointer to a field named container_field, within a structure of type container_type, and returns a pointer to the containing structure".

我的问题是:

  • 如果我们想要一个指向结构(即 container_type)的指针,我们可以 直接分配,对吗?
  • 那么为什么它的一个字段的指针被用来分配一个 整个结构的地址?
  • 谁能举例说明使用 那个宏?

请您参考如下方法:

举个例子:

struct A 
{ 
    int some_data; 
    int other_data; 
}; 

现在假设有这个函数:

int get_some_data_from_other(int *other) 
{ 
    struct A *a = container_of(other, struct A, other_data); 
    return a->some_data; 
} 

如您所见,我们可以通过知道指针所在结构的哪个字段来判断包含给定 int *other 的原始 struct A 是什么据说指向。 这里的关键是我们没有对结构本身的引用,而只是指向其成员之一的指针

这可能看起来很荒谬,但实际上在一些非常聪明的构造中很有用。一个非常常见的例子是内核创建链表的方式。我建议你阅读 this post on kernelnewbies.org .让我们看一个简短的例子:

struct whatever 
{ 
    /* whatever data */ 
    struct list_head mylist; 
}; 

所以 struct whatever 有一些数据,但它也想充当链表中的一个节点。他们在学校教你的是有一个不同的结构,它包含 next/prev 指针以及指向 struct whatever 的指针(或 void *)。这样,您就有了访问数据的节点。

按照所有软件工程标准,这实际上是一件好事。但是软件工程标准很少考虑 w.r.t 效率。参见 Why should I have written ZeroMQ in C, not C++ (part II) .

底线是,使用传统方法,您必须将链表节点与数据节点分开分配,也就是说,内存分配、释放、碎片和缓存未命中等开销加倍。 Linux 内核的做法恰恰相反。每个数据节点都包含一个通用链表节点。通用链表节点不知道有关数据或数据如何分配的任何信息,只知道如何连接到其他链表节点。

那么让我们深入了解一下:

 +-------------------+      +---------------------+      +---------------------+ 
 |                   |      |                     |      |                     | 
 |   WHATEVER DATA   |      |   WHATEVER DATA 2   |      |   WHATEVER DATA 3   | 
 |                   |      |                     |      |                     | 
 |                   |      |                     |      |                     | 
 |                   |      |                     |      |                     | 
 |                   |      |                     |      |                     | 
 +-------------------+      +---------------------+      +---------------------+ 
 |                   |----->|                     |----->|                     | 
 |       mylist      |      |       mylist 2      |      |       mylist 3      | 
 |                   |<-----|                     |<-----|                     | 
 +-------------------+      +---------------------+      +---------------------+ 

作为链表,您拥有的是 struct list_head 中指向其他 struct list_head 的指针。请注意,它们不指向 struct whatever,而是指向 mylist inside 这些结构。

假设你有一个节点,struct whatever what。你想找到下一个节点。你能做什么?首先,您可以执行 w.mylist.next 以获得指向下一个节点的 mylist 的指针。现在您必须能够提取包含该节点的实际 struct whatever。这就是使用 container_of 的地方:

struct whatever w_next = container_of(w.mylist.next, struct whatever, mylist); 

最后,注意Linux内核有宏可以遍历链表的所有节点,这往往是你想要的,所以你实际上不需要直接使用container_of自己。


标签:linux
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号