C语言 了解 Linux 内核中的 container_of 宏
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15832301/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Understanding container_of macro in the Linux kernel
提问by jaeyong
When I was browsing the Linux kernel, I found a container_ofmacro which is defined as follows:
我在浏览Linux内核的时候发现了一个container_of宏,定义如下:
#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?
这看起来像什么都不做。有人可以填补这里的空白吗?
回答by mikyra
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 devin your example refers to the name of pointer the second devrefers to the name of a structure member.
虽然dev您的示例中的第一个是指指针的名称,但第二个dev是指结构成员的名称。
Most probably this mix up is provoking all that headache. In fact the memberparameter in your quote refers to the name given to that member in the container structure.
很可能这种混淆引起了所有的头痛。事实上member,您引用中的参数是指在容器结构中为该成员提供的名称。
Taking this container for example:
以这个容器为例:
struct container {
int some_other_data;
int this_data;
}
And a pointer int *my_ptrto the this_datamember you'd use the macro to get a pointer to struct container *my_containerby using:
以及指向成员的指针int *my_ptr,this_data您将使用宏通过以下方式获取指针struct container *my_container:
struct container *my_container;
my_container = container_of(my_ptr, struct container, this_data);
Taking the offset of this_datato 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_datafrom your pointer my_ptrto get the correct location.
实际上,您只需this_data从指针中减去成员的偏移量my_ptr即可获得正确的位置。
That's exactly what the last line of the macro does.
这正是宏的最后一行所做的。
回答by Federico
The last sentence cast:
最后一句演员表:
(type *)(...)
a pointer to a given type. The pointer is calculated as offset from a given pointer dev:
指向给定 的指针type。指针计算为从给定指针的偏移量dev:
( (char *)__mptr - offsetof(type,member) )
When you use the cointainer_ofmacro, 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 twoin the structure:
您有一个指向结构中间的指针(并且您知道这是一个指向字段two[结构中的字段名称]的指针),但您想要检索整个结构 ( numbers)。因此,您计算two结构中字段的偏移量:
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.
并从给定的指针中减去这个偏移量。结果是指向结构开头的指针。最后,将此指针转换为结构类型以获得有效变量。
回答by shodanex
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 扩展的利用,statements 表达式。如果您将宏视为返回值的东西,那么最后一行将是:
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
25
回答by Spyder
conatainer_of() macro in Linux Kernel -
Linux 内核中的 conatainer_of() 宏 -
When it comes to managing several data structures in code, you'll almost always need to embed one structure into another and retrieve them at any moment without being asked questions about memory offsets or boundaries. Let's say you have a struct person, as defined here:
在代码中管理多个数据结构时,您几乎总是需要将一个结构嵌入到另一个结构中并随时检索它们,而不会被问到有关内存偏移或边界的问题。假设您有一个结构人,定义如下:
struct person {
int age;
int salary;
char *name;
} p;
By only having a pointer on age or salary, you can retrieve the whole structure wrapping (containing) that pointer. As the name says, the container_of macro is used to find the container of the given field of a structure. The macro is defined in include/linux/kernel.h and looks like the following:
通过只有一个关于年龄或薪水的指针,您可以检索包装(包含)该指针的整个结构。顾名思义,container_of 宏用于查找结构的给定字段的容器。该宏定义在 include/linux/kernel.h 中,如下所示:
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
Don't be afraid of the pointers; just see them as follows:
不要害怕指针;只需按如下方式查看它们:
container_of(pointer, container_type, container_field);
Here are the elements of the preceding code fragment:
以下是上述代码片段的元素:
- pointer: This is the pointer to the field in the structure
- container_type: This is the type of structure wrapping (containing) the pointer
- container_field: This is the name of the field to which pointer points inside the structure
- 指针:这是指向结构中字段的指针
- container_type:这是包装(包含)指针的结构类型
- container_field:这是指针指向结构内的字段的名称
Let's consider the following container:
让我们考虑以下容器:
struct person {
int age;
int salary;
char *name;
};
Now, let's consider one of its instances, along with a pointer to the age member:
现在,让我们考虑它的一个实例,以及一个指向 age 成员的指针:
struct person somebody;
[...]
int *age_ptr = &somebody.age;
Along with a pointer to the name member (age_ptr),you can use the container_of macro in order to get a pointer to the whole structure (container) that wraps this member by using the following:
除了指向名称成员 (age_ptr) 的指针外,您还可以使用 container_of 宏来获取指向包含该成员的整个结构(容器)的指针,方法如下:
struct person *the_person;
the_person = container_of(age_ptr, struct person, age);
container_of takes the offset of age at the beginning of the struct into account to get the correct pointer location. If you subtract the offset of the field age from the pointer age_ptr, you will get the correct location. This is what the macro's last line does:
container_of 会考虑结构开头处的 age 偏移量以获取正确的指针位置。如果从指针age_ptr中减去字段age的偏移量,就会得到正确的位置。这是宏的最后一行所做的:
(type *)( (char *)__mptr - offsetof(type,member) );
Applying this to a real example, gives the following:
将此应用于实际示例,给出以下内容:
struct family {
struct person *father;
struct person *mother;
int number_of_sons;
int family_id;
} f;
/*
* Fill and initialise f somewhere */ [...]
/*
* pointer to a field of the structure
* (could be any (non-pointer) member in the structure)
*/
int *fam_id_ptr = &f.family_id;
struct family *fam_ptr;
/* now let us retrieve back its family */
fam_ptr = container_of(fam_id_ptr, struct family, family_id);
The container_of macro is mainly used in generic containers in the kernel.
container_of 宏主要用于内核中的通用容器。
That's all about container_of macro in kernel.
这就是内核中的 container_of 宏的全部内容。
回答by qeatzy
A little real context says clearer, below use red-black tree as example, which is the
way that I understand container_of.
一点真实的上下文说的更清楚,下面以红黑树为例,这是我理解的方式container_of。
as Documentation/rbtree.txtstates, in linux kernel code, it's not rb_node contain data
entry, rather
正如Documentation/rbtree.txt所述,在 linux 内核代码中,它不是 rb_node 包含数据条目,而是
Data nodes in an rbtree tree are structures containing a struct rb_node member.
rbtree 树中的数据节点是包含 struct rb_node 成员的结构。
struct vm_area_struct(in file include/linux/mm_types.h:284) is such a structure,
struct vm_area_struct(in file include/linux/mm_types.h:284) 是这样的结构,
in the same
file, there is a macro rb_entrywhich is defined as
在同一个文件中,有一个宏rb_entry定义为
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
clearly, rb_entryis same as container_of.
显然,rb_entry与 相同container_of。
at mm/mmap.c:299inside 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, member),
typeis the container struct, herestruct vm_area_structmemberis name of a member oftypeinstance, herevm_rb, which is of typerb_node,ptris a pointer pointingmemberof antypeinstance, hererb_node *nd.
type是容器结构,这里struct vm_area_structmember是type实例成员的名称,此处为vm_rb,其类型为rb_node,ptr是指向指针member的的type情况下,在这里rb_node *nd。
what container_ofdo is, as in this example,
什么container_of做的是,在这个例子中,
- given address of
obj.member(hereobj.vm_rb), return the address ofobj. - since a struct is a block of contiguous memory, address of
obj.vm_rbminusoffset between the struct and memberwill be the container's address.
- 给定
obj.member(此处obj.vm_rb)的地址,返回 的地址obj。 - 由于结构是连续的存储器的块,的地址
obj.vm_rb减去offset between the struct and member将成为容器的地址。
include/linux/kernel.h:858-- definition of container_of
include/linux/kernel.h:858-- 定义 container_of
include/linux/rbtree.h:51-- definition of rb_entry
include/linux/rbtree.h:51-- 定义 rb_entry
mm/mmap.c:299-- usage of rb_entry
mm/mmap.c:299-- 用法 rb_entry
include/linux/mm_types.h:284-- struct vm_area_struct
include/linux/mm_types.h:284—— struct vm_area_struct
Documentation/rbtree.txt:-- Documentation of red-black tree
Documentation/rbtree.txt:-- 红黑树文档
include/linux/rbtree.h:36-- definition of struct rb_node
include/linux/rbtree.h:36-- 定义 struct rb_node
P.S.
聚苯乙烯
Above files are in current develop version, i.e, 4.13.0-rc7.
以上文件是当前的开发版本,即4.13.0-rc7.
file:kmean kth line in file.
file:k中的平均第 k 行file。
回答by andy
Very useful link for understanding container_of macro in linux kernel. https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html
用于理解 linux 内核中的 container_of 宏的非常有用的链接。 https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html
回答by Alok Prasad
Most Simplest Implementation of Container _of macro is below , It reduces all complex checking of type and works
Container _of 宏最简单的实现如下,它减少了所有复杂的类型检查和工作
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
ptr will give address of member and just subtract offset difference and you will get start address.
ptr 将给出成员的地址并减去偏移量差,您将获得起始地址。
Example usage
示例用法
struct sample {
int mem1;
char mem2;
int mem3;
};
int main(void)
{
struct sample sample1;
printf("Address of Structure sample1 (Normal Method) = %p\n", &sample1);
printf("Address of Structure sample1 (container_of Method) = %p\n",
container_of(&sample1.mem3, struct sample, mem3));
return 0;
}

