怀疑linux中的container_of宏

时间:2014-03-28 11:32:25

标签: c macros linux-kernel linux-device-driver embedded-linux

为什么我们使用container_of宏?

container_of(pointer, container_type, container_field);

在LDD中说

  

"此宏采用指向名为container_field的字段的指针   类型为container_type的结构,并返回指向的结构   包含结构"。

我的问题是:

  • 如果我们想要一个指向结构的指针(即container_type),我们可以 直接分配,对吧?
  • 然后为什么其中一个字段的指针被指定为 解决整个结构?
  • 任何人都可以用一个例子来说明使用的优点 那宏?

3 个答案:

答案 0 :(得分:3)

让我举个例子:

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;
}

正如您所看到的,我们可以通过知道指针所指向的结构的哪个字段来告诉包含给定struct A的原始int *other是什么。 这里的关键是我们不要引用结构本身,而只是指向其中一个成员的指针

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

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

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

按照所有软件工程标准,这实际上是一件好事。但是,软件工程标准对于效率的影响很小。请参阅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,而是指向<{1}} 里面这些结构。

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

container_of

最后,请注意Linux内核有遍历链表的所有节点的宏,这通常不是你想要的,所以你实际上并不需要直接使用struct whatever w_next = container_of(w.mylist.next, struct whatever, mylist);

答案 1 :(得分:1)

Why do we use container_of macro ?

宏的容器用于获取指向包含元素的结构的开头的指针。

例如

struct container {
  int some_other_data;
  int this_data;
}

指向int *my_ptr成员的指针this_data,您可以使用宏来获取指向结构容器*my_container的指针:

struct container *my_container;
my_container = container_of(my_ptr, struct container, this_data);

this_data的偏移量考虑到结构的开头对于获取正确的指针位置至关重要。

实际上,您只需从指针this_data中减去成员my_ptr的偏移量即可获得正确的位置。

另见here,明确你的疑惑。

答案 2 :(得分:0)

是的,您可以使用自己编写的演员/作业,但它有一些优势。

效率 - 使用宏而不是声明整个演员表并不容易吗?

维护 - 如果每个人都使用宏,只需编辑宏,就可以在整个项目中轻松更改指针分配的方式。

可读性 - 什么是易读?一个明确的演员或宏为你做的吗?

安全性 - 您可能相信此宏可以运行,并且它运行任何重要的强制转换和类型检查,比您自己的代码更好。