了解Linux内核中的container_ofmacros
当我浏览Linux内核时,发现了一个container_of
macros,其定义如下:
#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_of
macros时,您想要检索包含给定字段的指针的结构。 例如:
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_entry
与container_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
-
member
是type
实例的成员的名称,这里是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:858
– container_of
定义
include/linux/rbtree.h:51
– rb_entry
定义
mm/mmap.c:299
– 使用rb_entry
include/linux/mm_types.h:284
– struct vm_area_struct
Documentation/rbtree.txt:
– 红黑树的文档
include/linux/rbtree.h:36
– struct rb_node
定义
PS
以上文件在当前开发版本中,即4.13.0-rc7
。
file:k
表示file
第k行。