当我浏览Linux内核时,我找到了一个container_of
宏,其定义如下:
#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) );})
如果我们按如下方式使用宏:
container_of(dev, struct wifi_device, dev);
最后一句的相应部分是:
(struct wifi_device *)( (char *)__mptr - offset(struct wifi_device, dev);
看起来什么都不做。 有人可以在这里填补空白吗?
答案 0 :(得分:72)
您的使用示例container_of(dev, struct wifi_device, dev);
可能有点误导,因为您在那里混合了两个名称空间。
虽然示例中的第一个dev
引用了指针的名称,但第二个dev
引用了结构成员的名称。
最有可能这种混乱引起了所有人的头痛。实际上,引用中的member
参数是指容器结构中为该成员指定的名称。
以此容器为例:
struct container {
int some_other_data;
int this_data;
}
指向int *my_ptr
成员的指针this_data
,您可以使用宏来获取指向struct container *my_container
的指针:
struct container *my_container;
my_container = container_of(my_ptr, struct container, this_data);
将this_data
的偏移量考虑到结构的开头对于获取正确的指针位置至关重要。
实际上,您只需从指针this_data
中减去成员my_ptr
的偏移量即可获得正确的位置。
这正是宏的最后一行所做的。
答案 1 :(得分:15)
最后一句话:
(type *)(...)
指向给定type
的指针。指针计算为给定指针dev
的偏移量:
( (char *)__mptr - offsetof(type,member) )
使用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);
你有一个指向结构中间的指针(你知道这是一个指向归档two
[结构中的字段名称]的指针),但你想要检索整个结构(numbers
)。因此,您计算结构中two
字段的偏移量:
offsetof(type,member)
并从给定指针中减去此偏移量。结果是指向结构开始的指针。最后,将此指针强制转换为结构类型以获得有效变量。
答案 2 :(得分:8)
使用gcc扩展名statements expressions。如果您将宏视为返回值的内容,则最后一行将是:
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
答案 3 :(得分:2)
一些真实的背景说清楚,在下使用红黑树作为例子,这是
我理解的方式container_of
。
作为Documentation/rbtree.txt
状态,在linux内核代码中,它不是rb_node包含数据
进入,而不是
rbtree树中的数据节点是包含结构的结构 rb_node成员。
struct vm_area_struct
(在文件include/linux/mm_types.h:284
中)就是这样一种结构,
同样的
文件,有一个宏rb_entry
,定义为
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
显然,rb_entry
与container_of
相同。
在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 */
现在很明显,在container_of(ptr, type, member)
,
type
是容器结构,此处为struct vm_area_struct
member
是type
个实例的成员名称,此处为vm_rb
,其类型为rb_node
,ptr
是指向member
个实例的type
的指针,此处为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
<强> P.S。强>
以上文件属于当前开发版本,即4.13.0-rc7
。
file:k
表示file
中的第k行。
答案 4 :(得分:1)
conatainer_of()宏-
在代码中管理多个数据结构时,几乎总是需要将一个结构嵌入另一个结构中并随时检索它们,而不会被问到有关内存偏移量或边界的问题。假设您有一个struct person,定义如下:
struct person {
int age;
int salary;
char *name;
} p;
通过仅具有年龄或薪水的指针,您可以检索包装(包含)该指针的整个结构。顾名思义,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)); })
不要害怕指针;只是看到它们如下:
container_of(pointer, container_type, container_field);
以下是前面代码片段的元素:
让我们考虑以下容器:
struct person {
int age;
int salary;
char *name;
};
现在,让我们考虑其实例之一,以及指向该年龄成员的指针:
struct person somebody;
[...]
int *age_ptr = &somebody.age;
连同指向名称成员(age_ptr)的指针,您可以使用container_of宏来获取指向包装该成员的整个结构(容器)的指针,方法如下:
struct person *the_person;
the_person = container_of(age_ptr, struct person, age);
container_of考虑到结构开始处的年龄偏移,以获取正确的指针位置。如果从指针age_ptr中减去字段年龄的偏移量,则将获得正确的位置。这是宏的最后一行:
(type *)( (char *)__mptr - offsetof(type,member) );
将其应用于一个真实的示例,将给出以下内容:
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);
container_of宏主要用于内核中的通用容器。
关于内核中的container_of宏。
答案 5 :(得分:0)
一个非常有用的链接,用于了解Linux内核中的container_of宏。 https://linux-concepts.blogspot.com/2018/01/understanding-containerof-macro-in.html
答案 6 :(得分:0)
容器_of宏的最简单实现是在下面,它减少了类型和工作的所有复杂检查
__next__
ptr将给出成员的地址,然后减去偏移量差,您将 获取起始地址。
示例用法
__iter__