了解Linux内核中的container_ofmacros

当我浏览Linux内核时,发现了一个container_ofmacros,其定义如下:

 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) 

我明白container_of是做什么的,但是我不明白的是最后一个句子

 (type *)( (char *)__mptr - offsetof(type,member) );}) 

如果我们使用macros,如下所示:

 container_of(dev, struct wifi_device, dev); 

最后一句的相应部分是:

 (struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev); 

这看起来像什么都不做。 有人可以填补这个空白吗?

您的使用示例container_of(dev, struct wifi_device, dev); 可能有点误导,因为您在那里混合了两个名称空间。

虽然你的例子中的第一个dev指向指针的名字,但第二个dev指向一个结构成员的名字。

很可能这混在一起引起所有的头痛。 实际上,引用中的member参数指的是在容器结构中给予该成员的名称。

以这个容器为例:

 struct container { int some_other_data; int this_data; } 

而一个int *my_ptr指针指向this_data成员,您将使用该macros来获取指向struct container *my_container的指针:

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

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

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

这正是macros的最后一行。

最后一句:

 (type *)(...) 

指向给定type的指针。 指针计算为偏离给定的指针dev

 ( (char *)__mptr - offsetof(type,member) ) 

当您使用cointainer_ofmacros时,您想要检索包含给定字段的指针的结构。 例如:

 struct numbers { int one; int two; int three; } n; int *ptr = &n.two; struct numbers *n_ptr; n_ptr = container_of(ptr, struct numbers, two); 

你有一个指向结构中间的指针(你知道这是一个指向two 字段(结构中的字段名 )的指针),但是你想要检索整个结构( numbers )。 所以,你计算结构中的two字段的偏移量:

 offsetof(type,member) 

并从给定的指针中减去这个偏移量。 结果是指向结构开始的指针。 最后,您将此指针转换为结构types以具有有效的variables。

这是一个gcc扩展的使用, 语句expression式 。 如果你看到macros返回一个值,那么最后一行是:

 return (struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev); 

请参阅链接页面以获取复合语句的解释。 这里是一个例子:

 int main(int argc, char**argv) { int b; b = 5; b = ({int a; a = b*b; a;}); printf("b %d\n", b); } 

输出是

b 25

有一点真实的背景说得更清楚,下面以红黑树为例 ,这是我理解container_of

作为Documentation/rbtree.txt指出,在linux内核代码中,不是rb_node包含数据条目,而是

rbtree树中的数据节点是包含struct rb_node成员的结构。

struct vm_area_struct (在文件include/linux/mm_types.h:284 )就是这样一种结构,

在同一个文件中,有一个macros定义为rb_entry

 #define rb_entry(ptr, type, member) container_of(ptr, type, member) 

显然, rb_entrycontainer_of相同。

在函数定义browse_rb中的mm/mmap.c:299 browse_rb ,有一个rb_entry的用法:

 static int browse_rb(struct mm_struct *mm) { /* two line code not matter */ struct rb_node *nd, *pn = NULL; /*nd, first arg, ie ptr. */ unsigned long prev = 0, pend = 0; for (nd = rb_first(root); nd; nd = rb_next(nd)) { struct vm_area_struct *vma; vma = rb_entry(nd, struct vm_area_struct, vm_rb); /* -- usage of rb_entry (equivalent to container_of) */ /* more code not matter here */ 

现在很明显,在container_of(ptr, type, member)

  • type是容器结构,这里是struct vm_area_struct
  • membertype实例的成员的名称,这里是vm_rb ,types为rb_node
  • ptr是一个type实例的指针member ,这里是rb_node *nd

在这个例子中, container_of是什么,

  • obj.member (这里是obj.vm_rb )的地址,返回obj的地址。
  • 由于结构是一个连续的内存块, obj.vm_rb减去offset between the struct and member地址将是容器的地址。

include/linux/kernel.h:858container_of定义

include/linux/rbtree.h:51rb_entry定义

mm/mmap.c:299 – 使用rb_entry

include/linux/mm_types.h:284struct vm_area_struct

Documentation/rbtree.txt: – 红黑树的文档

include/linux/rbtree.h:36struct rb_node定义

PS

以上文件在当前开发版本中,即4.13.0-rc7

file:k表示file第k行。