理解Linux内核中的container_of宏

时间:2023-01-26 16:04:40

When I was browsing the Linux kernel, I found a container_of macro which is defined as follows:

当我浏览Linux内核时,我发现了一个宏,它的定义如下:

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

I understand what does container_of do, but what I do not understand is the last sentence, which is

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

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

If we use the macro as follows:

如果我们使用宏,如下:

container_of(dev, struct wifi_device, dev);

The corresponding part of the last sentence would be:

最后一句的对应部分是:

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

which looks like doing nothing.Could anybody please fill the void here?

看起来什么都不做。谁能填补这一空缺吗?

4 个解决方案

#1


56  

Your usage example container_of(dev, struct wifi_device, dev); might be a bit misleading as you are mixing two namespaces there.

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

While the first dev in your example refers to the name of pointer the second dev refers to the name of a structure member.

当示例中的第一个dev引用指针的名称时,第二个dev引用结构成员的名称。

Most probably this mix up is provoking all that headache. In fact the member parameter in your quote refers to the name given to that member in the container structure.

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

Taking this container for example:

以这个容器为例:

struct container {  int some_other_data;  int this_data;}

And a pointer int *my_ptr to the this_data member you'd use the macro to get a pointer to struct container *my_container by using:

一个指向this_data成员的指针int *my_ptr你可以用这个宏来获取指向struct container *my_container的指针,方法是:

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

Taking the offset of this_data to the beginning of the struct into account is essential to getting the correct pointer location.

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

Effectively you just have to subtract the offset of the member this_data from your pointer my_ptr to get the correct location.

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

That's exactly what the last line of the macro does.

这正是宏的最后一行所做的。

#2


14  

The last sentence cast:

最后一句话演员:

(type *)(...)

a pointer to a given type. The pointer is calculated as offset from a given pointer dev:

指向给定类型的指针。指针计算为给定指针dev的偏移量:

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

When you use the cointainer_of macro, you want to retrieve the structure that contains the pointer of a given field. For example:

当您使用cointainer_of宏时,您希望检索包含给定字段指针的结构。例如:

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

You have a pointer that points in the middle of a structure (and you know that is a pointer to the filed two [the field name in the structure]), but you want to retrieve the entire structure (numbers). So, you calculate the offset of the filed two in the structure:

您有一个指向结构中间的指针(并且您知道这是指向存档的两个(结构中的字段名)的指针),但是您希望检索整个结构(数字)。因此,你计算了结构中归档2的偏移量:

offsetof(type,member)

and subtract this offset from the given pointer. The result is the pointer to the start of the structure. Finally, you cast this pointer to the structure type to have a valid variable.

从给定的指针中减去这个偏移量。结果是指向结构开始的指针。最后,您将这个指针转换为结构类型,以拥有一个有效的变量。

#3


7  

It is an utilisation of a gcc extension, the statements expressions. If you see the macro as something returning a value, then the last line would be :

它是一个gcc扩展的使用,语句表达式。如果您将宏视为返回值的对象,那么最后一行将是:

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

See the linked page for an explanation of compound statements. Here is an example :

有关复合语句的解释,请参阅链接页面。这里有一个例子:

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

The output is

输出是

b 25

b 25

#4


0  

A little real context says clearer, below use red-black tree as example, which is theway that I understand container_of.

一个小的实际上下文说的更清楚,下面使用红黑树作为例子,这是我理解container_of的方式。

as Documentation/rbtree.txt states, in linux kernel code, it's not rb_node contain dataentry, rather

作为文档/ rbtree。txt表示,在linux内核代码中,它不是包含数据条目的rb_node

Data nodes in an rbtree tree are structures containing a struct rb_node member.

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

struct vm_area_struct (in file include/linux/mm_types.h:284) is such a structure,

struct vm_area_struct(在文件中包含/linux/mm_types.h:284)是这样一个结构,

in the samefile, there is a macro rb_entry which is defined as

在samefile中,有一个宏rb_entry,定义为

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

clearly, rb_entry is same as container_of.

显然,rb_entry与container_of相同。

at mm/mmap.c:299 inside function definition browse_rb, there is a usage of rb_entry:

在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, i.e. 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 */

now it is clear, in container_of(ptr, type, member),

很明显,在container_of(ptr,类型,成员)中,

  • type is the container struct, here struct vm_area_struct
  • 类型是容器结构体,这里是结构体vm_area_struct
  • member is name of a member of type instance, here vm_rb, which is of type rb_node,
  • 成员是类型实例的成员的名称,这里是vm_rb,类型是rb_node,
  • ptr is a pointer pointing member of an type instance, here rb_node *nd.
  • ptr是一个类型实例的指针指向成员,这里是rb_node *nd。

what container_of do is, as in this example,

container_of是,如本例中所示,

  • given address of obj.member (here obj.vm_rb), return theaddress of obj.
  • 给定地址的obj。成员(这里是object .vm_rb),返回obj的地址。
  • since a struct is a block of contiguous memory, address of obj.vm_rb minusoffset between the struct and member will be the container's address.
  • 由于结构体是一个连续内存块,所以是obj的地址。在结构体和成员之间的vm_rb minusoffset将是容器的地址。

include/linux/kernel.h:858 -- definition of container_of

包括/ linux /内核。h:858——container_of的定义

include/linux/rbtree.h:51 -- definition of rb_entry

包括/ linux / rbtree。h:51——rb_entry的定义

mm/mmap.c:299 -- usage of rb_entry

毫米/ mmap。c:299——rb_entry的使用

include/linux/mm_types.h:284 -- struct vm_area_struct

包括/ linux / mm_types。h:284——struct vm_area_struct

Documentation/rbtree.txt: -- Documentation of red-black tree

文档/ rbtree。txt:——红黑树的文档

include/linux/rbtree.h:36 -- definition of struct rb_node

包括/ linux / rbtree。h:36——结构rb_node的定义

P.S.

注:

Above files are in current develop version, i.e, 4.13.0-rc7.

以上文件均为当前开发版本i。e 4.13.0-rc7。

file:k mean kth line in file.

文件:k表示文件中的第k行。

#1


56  

Your usage example container_of(dev, struct wifi_device, dev); might be a bit misleading as you are mixing two namespaces there.

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

While the first dev in your example refers to the name of pointer the second dev refers to the name of a structure member.

当示例中的第一个dev引用指针的名称时,第二个dev引用结构成员的名称。

Most probably this mix up is provoking all that headache. In fact the member parameter in your quote refers to the name given to that member in the container structure.

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

Taking this container for example:

以这个容器为例:

struct container {  int some_other_data;  int this_data;}

And a pointer int *my_ptr to the this_data member you'd use the macro to get a pointer to struct container *my_container by using:

一个指向this_data成员的指针int *my_ptr你可以用这个宏来获取指向struct container *my_container的指针,方法是:

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

Taking the offset of this_data to the beginning of the struct into account is essential to getting the correct pointer location.

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

Effectively you just have to subtract the offset of the member this_data from your pointer my_ptr to get the correct location.

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

That's exactly what the last line of the macro does.

这正是宏的最后一行所做的。

#2


14  

The last sentence cast:

最后一句话演员:

(type *)(...)

a pointer to a given type. The pointer is calculated as offset from a given pointer dev:

指向给定类型的指针。指针计算为给定指针dev的偏移量:

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

When you use the cointainer_of macro, you want to retrieve the structure that contains the pointer of a given field. For example:

当您使用cointainer_of宏时,您希望检索包含给定字段指针的结构。例如:

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

You have a pointer that points in the middle of a structure (and you know that is a pointer to the filed two [the field name in the structure]), but you want to retrieve the entire structure (numbers). So, you calculate the offset of the filed two in the structure:

您有一个指向结构中间的指针(并且您知道这是指向存档的两个(结构中的字段名)的指针),但是您希望检索整个结构(数字)。因此,你计算了结构中归档2的偏移量:

offsetof(type,member)

and subtract this offset from the given pointer. The result is the pointer to the start of the structure. Finally, you cast this pointer to the structure type to have a valid variable.

从给定的指针中减去这个偏移量。结果是指向结构开始的指针。最后,您将这个指针转换为结构类型,以拥有一个有效的变量。

#3


7  

It is an utilisation of a gcc extension, the statements expressions. If you see the macro as something returning a value, then the last line would be :

它是一个gcc扩展的使用,语句表达式。如果您将宏视为返回值的对象,那么最后一行将是:

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

See the linked page for an explanation of compound statements. Here is an example :

有关复合语句的解释,请参阅链接页面。这里有一个例子:

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

The output is

输出是

b 25

b 25

#4


0  

A little real context says clearer, below use red-black tree as example, which is theway that I understand container_of.

一个小的实际上下文说的更清楚,下面使用红黑树作为例子,这是我理解container_of的方式。

as Documentation/rbtree.txt states, in linux kernel code, it's not rb_node contain dataentry, rather

作为文档/ rbtree。txt表示,在linux内核代码中,它不是包含数据条目的rb_node

Data nodes in an rbtree tree are structures containing a struct rb_node member.

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

struct vm_area_struct (in file include/linux/mm_types.h:284) is such a structure,

struct vm_area_struct(在文件中包含/linux/mm_types.h:284)是这样一个结构,

in the samefile, there is a macro rb_entry which is defined as

在samefile中,有一个宏rb_entry,定义为

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

clearly, rb_entry is same as container_of.

显然,rb_entry与container_of相同。

at mm/mmap.c:299 inside function definition browse_rb, there is a usage of rb_entry:

在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, i.e. 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 */

now it is clear, in container_of(ptr, type, member),

很明显,在container_of(ptr,类型,成员)中,

  • type is the container struct, here struct vm_area_struct
  • 类型是容器结构体,这里是结构体vm_area_struct
  • member is name of a member of type instance, here vm_rb, which is of type rb_node,
  • 成员是类型实例的成员的名称,这里是vm_rb,类型是rb_node,
  • ptr is a pointer pointing member of an type instance, here rb_node *nd.
  • ptr是一个类型实例的指针指向成员,这里是rb_node *nd。

what container_of do is, as in this example,

container_of是,如本例中所示,

  • given address of obj.member (here obj.vm_rb), return theaddress of obj.
  • 给定地址的obj。成员(这里是object .vm_rb),返回obj的地址。
  • since a struct is a block of contiguous memory, address of obj.vm_rb minusoffset between the struct and member will be the container's address.
  • 由于结构体是一个连续内存块,所以是obj的地址。在结构体和成员之间的vm_rb minusoffset将是容器的地址。

include/linux/kernel.h:858 -- definition of container_of

包括/ linux /内核。h:858——container_of的定义

include/linux/rbtree.h:51 -- definition of rb_entry

包括/ linux / rbtree。h:51——rb_entry的定义

mm/mmap.c:299 -- usage of rb_entry

毫米/ mmap。c:299——rb_entry的使用

include/linux/mm_types.h:284 -- struct vm_area_struct

包括/ linux / mm_types。h:284——struct vm_area_struct

Documentation/rbtree.txt: -- Documentation of red-black tree

文档/ rbtree。txt:——红黑树的文档

include/linux/rbtree.h:36 -- definition of struct rb_node

包括/ linux / rbtree。h:36——结构rb_node的定义

P.S.

注:

Above files are in current develop version, i.e, 4.13.0-rc7.

以上文件均为当前开发版本i。e 4.13.0-rc7。

file:k mean kth line in file.

文件:k表示文件中的第k行。