背景
在内核中经常需要做指针偏移。往往是不同模块有自己的struct结构体B,这个结构体实际内存地址是被上级结构体A的一个成员。如何通过这个B获取到A。
举个例子:
struct A {
int mem_a;
struct B mem_b;
};
struct A enty_a;
假设知道mem_b的地址,可以得到enty_a。
一个闭环
从A获取B就用 a->b取指针就可以了,编译器会自动计算。
但是如果要从b获取a,就得计算,也就是先获取b地址Addr1,然后获取a到b的偏移offset2,然后用Addr1-offset2得到地址就是a的地址。
a到b的偏移如何获取呢?
假设有一个struct a的结构,起始地址是0,那么a到b的偏移就是a->b
代码
#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char *)(ptr) - (char *) &((type *)0)->member)
#endif
可以看到prt是 已知地址,然后减去偏移,偏移是用从地址为0的struct a(也就是图中的type)指向对应的member
用法
1
struct config_group *group =
container_of(item, struct config_group, cg_item);
struct cma_dev_group *cma_dev_group =
container_of(group, struct cma_dev_group, device_group);
实际结构:
struct config_group {
struct config_item cg_item;
struct list_head cg_children;
struct configfs_subsystem *cg_subsys;
struct list_head default_groups;
struct list_head group_entry;
};
图示:

可以看到这里的config_group和config_item是相互关系,item是group的一个成员。并在在group的struct item成员叫做cg_item,根据前面的分析的。假设指导子成员cg_item的地址,要得到父的base地址就用container_of获取,第一个参数是已知地址,然后用类似struct config_group->cg_item方式获取偏移,从而计算出group地址。
struct config_group *group =
container_of(item, struct config_group, cg_item);
所以看到container_of三个参数,分别是已知地址,然后减去后面两个参数组合的struct的offset,并且是struct的名字和成员变量。这样就不容易搞混了。
2
struct cma_dev_group *cma_dev_group =
container_of(group, struct cma_dev_group, device_group);
根据阶段1获取到了group地址后,继续使用相同的方法获取到cma_dev_group的地址。
值得注意的是这里cma_dev_group是mlx的一个内核模块的成员,他内部使用了kernel的group。但是实际在container_of在使用的时候,看到的已知地址不关注他是什么struct,都会被转义成char *,然后用这个base - offset

用户态库使用它的一个例子
这是来自于rshim的代码
#ifndef container_of
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof(type, member))); })
#endif�
综述
container_of三个参数,分别是已知地址,然后减去后面两个参数组合的struct的offset,并且是struct的名字和成员变量。这样就不容易搞混了。

&spm=1001.2101.3001.5002&articleId=145021049&d=1&t=3&u=38777b19719a4ec085eed61b552c0294)
1725

被折叠的 条评论
为什么被折叠?



