当前位置:   article > 正文

Linux kernel container_of 宏_linux kernel container of

linux kernel container of

 

目录

基本理解container_of 宏

深入探究container_of 宏

container_of 宏 版本一

container_of 宏 版本二

container_of 宏 版本三


基本理解container_of 宏

container_of,顾名思义就是某某某的容器,也就是说某某某成员变量所在的结构体是谁。

  1. struct cntner {
  2. int i; char c; float f;
  3. };
  4. struct cntner init_struct ={.i=2019,'a',0.0120};
  5. char *pc = &init_struct.c;
  6. // 参数1:指向该结构体成员变量的指针;参数2:该结构体变量类型;参数3:该结构体成员变量名
  7. struct cntner *pcntner = container_of(pc, struct cntner, c);
  8. // 然后就可以使用pcntner结构体指针寻找其他成员的值了
  9. printf("init_struct.i = %d\n", pcntner->i);
  10. printf("init_struct.c = %c\n", pcntner->c);
  11. printf("init_struct.f = %f\n", pcntner->f);

有人会问为什么不直接使用结构体指针访问成员变量呢?其实内核为了方便管理自己的数据结构,经常采用链表或者红黑树。这样很多时候,我们只能获得链表或者红黑树节点。这种情况下我们就可以通过container_of宏获得结构体指针,从而很容易访问结构体的其他成员。

简单举一个例子:

  1. static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
  2. {
  3. struct fb_event *evdata = data;
  4. int *blank;
  5. struct xxx_ts_data *xxx_data = container_of(self, struct xxx_ts_data, fb_notif);
  6. ...
  7. }
一般会使用这个宏接口就行了,若不想放弃请看下面深入探究container_of 宏。

深入探究container_of 宏

计算出结构体cntner 的地址其实就是通过变量的地址减去自己的偏移量,就这么简单。

cntner_addr = addr_c - offsetof_c

其中变量c的偏移量offsetof_c = &((struct cntner *)0)->c0被强制转化为结构体是不是很高级,其实就是起始地址为0的结构体,然后指向变量c取地址就是c的偏移量。

我们看看下面几个container_of 宏的实现:

container_of 宏 版本一

这个宏定义出自 msm-4.9\drivers\gpu\drm\nouveau\include\nvif\list.h

  1. /**
  2. * Returns a pointer to the container of this list element.
  3. *
  4. * Example:
  5. * struct foo* f;
  6. * f = container_of(&foo->entry, struct foo, entry);
  7. * assert(f == foo);
  8. *
  9. * @param ptr Pointer to the struct list_head.
  10. * @param type Data type of the list element.
  11. * @param member Member name of the struct list_head field in the list element.
  12. * @return A pointer to the data struct containing the list head.
  13. */
  14. #ifndef container_of
  15. #define container_of(ptr, type, member) \
  16. (type *)((char *)(ptr) - (char *) &((type *)0)->member)
  17. #endif

下面我们编写一个测试代码

  1. #include <stdio.h>
  2. #define container_of(ptr, type, member) ( (type*) ((char*)(ptr) - ((char*)&((type*)0)->member)) )
  3. struct cntner {
  4. int i; char c; float f;
  5. };
  6. int main(void)
  7. {
  8. struct cntner init_struct ={.i=2019,'c',2019.0122};
  9. char *pc = &init_struct.c;
  10. struct cntner *pcntner = container_of(pc, struct cntner, c);
  11. printf(" &init_struct = %p\n", &init_struct);
  12. printf(" = pcntner = %p\n", pcntner);
  13. printf(" (pc) = %p\n", (pc) );
  14. printf("((char*)&((struct cntner*)0)->c) = %p\n", ((char*)&((struct cntner*)0)->c) );
  15. //warning: format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘long int’
  16. //printf(" = %p\n", ((char*)(pc) - ((char*)&((struct cntner*)0)->c)) );
  17. printf("(struct cntner*) ((char*)(pc) - ((char*)&((struct cntner*)0)->c)) = %p\n", \
  18. (struct cntner*)((char*)(pc) - ((char*)&((struct cntner*)0)->c)) );
  19. // output other people
  20. printf("init_struct.i = %d\n", pcntner->i);
  21. printf("init_struct.c = %c\n", pcntner->c);
  22. printf("init_struct.f = %f\n", pcntner->f);
  23. return 0;
  24. }

运行结果如下,读者用其他变量可以测试一下container_of

  1. ubuntu14:~/Codes/test$ gcc container_of.c
  2. ubuntu14:~/Codes/test$ ./a.out
  3. &init_struct = 0x7ffe1a9a7d80
  4. = pcntner = 0x7ffe1a9a7d80
  5. (pc) = 0x7ffe1a9a7d84
  6. ((char*)&((struct cntner*)0)->c) = 0x4
  7. (struct cntner*) ((char*)(pc) - ((char*)&((struct cntner*)0)->c)) = 0x7ffe1a9a7d80
  8. init_struct.i = 2019
  9. init_struct.c = c
  10. init_struct.f = 2019.012207

可以用预处理命令查看宏替换结果(预处理命令请参看文章 C语言编译流程

  1. ubuntu14:~/Codes/test$ gcc -E -o container_of.i container_of.c
  2. ubuntu14:~/Codes/test$ cat container_of.i
  3. ... ...
  4. int main(void)
  5. {
  6. struct cntner init_struct ={.i=2019,'c',2019.0122};
  7. char *pc = &init_struct.c;
  8. struct cntner *pcntner = ( (struct cntner*) ((char*)(pc) - ((char*)&((struct cntner*)0)->c)) );
  9. ... ...
上面用都把地址用(char*)转换,是以防类型不一致导致编译错误。

container_of 宏 版本二

这个宏定义出自 msm-4.9\include\linux\kernel.h,版本二的 container_of(ptr, type, member)宏的实现包括两部分:

  • 做类型检查。检查ptr是否是指向结构成员member的指针,如果我们用typeof求出来的类型和ptr不一致,那么编译器会报错,因为ptr和member都是手工输入的参数,宏要保证它们是结构体成员和其相关联指针的一致性。
  • 计算size大小。结构体的起始地址 = (type *)((char *)mptr - offset) (注:强转为该结构体指针)
  1. /**
  2. - container_of - cast a member of a structure out to the containing structure
  3. - @ptr: the pointer to the member.
  4. - @type: the type of the container struct this is embedded in.
  5. - @member: the name of the member within the struct.
  6. - */
  7. #define container_of(ptr, type, member) ({ \
  8. const typeof( ((type *)0)->member ) *__mptr = (ptr); \
  9. (type *)( (char *)__mptr - offsetof(type,member) );})
  10. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

替换结果为:

  1. int main(void)
  2. {
  3. struct cntner init_struct ={.i=2019,'c',2019.0122};
  4. char *pc = &init_struct.c;
  5. struct cntner *pcntner = ({ const typeof(((struct cntner *)0)->c) *__mptr=(pc); (struct cntner *) ((char *)__mptr - ((size_t) &((struct cntner *)0)->c)); });

container_of 宏 版本三

kernel 4.12 的该宏,发现有2个新变化,显得更加高大上了.

  • 用void *取代了char *来做减法;
  • 用__same_type宏来做类型检测,显得更加的直观明了,错了还可以有提示。
  1. /**
  2. * container_of - cast a member of a structure out to the containing structure
  3. * @ptr: the pointer to the member.
  4. * @type: the type of the container struct this is embedded in.
  5. * @member: the name of the member within the struct.
  6. *
  7. */
  8. #define container_of(ptr, type, member) ({ \
  9. void *__mptr = (void *)(ptr); \
  10. BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
  11. !__same_type(*(ptr), void), \
  12. "pointer type mismatch in container_of()"); \
  13. ((type *)(__mptr - offsetof(type, member)));
  14. // include/linux/compiler.h
  15. /* Are two types/vars the same type (ignoring qualifiers)? */
  16. #ifndef __same_type
  17. # define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
  18. #endif

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/782689
推荐阅读
相关标签
  

闽ICP备14008679号