查看: 891|回复: 2

[分享] Linux驱动中container_of的作用

[复制链接]

该用户从未签到

656

主题

6312

帖子

0

超级版主

Rank: 8Rank: 8

积分
19944
最后登录
2024-4-19
发表于 2021-11-22 13:23:59 | 显示全部楼层 |阅读模式
​container_of的作用的通过结构体成员变量地址获取这个结构体的地址。内核函数调用常常给函数传入的是结构体成员地址,然后在函数里面又想使用这个结构体里面的其他成员变量,所以就引发了问题。
  1. static void sensor_suspend(struct early_suspend *h)                  
  2. {                                                                     
  3.     struct sensor_private_data *sensor =                              
  4.             container_of(h, struct sensor_private_data, early_suspend);   
  5.     if (sensor->ops->suspend)                                         
  6.         sensor->ops->suspend(sensor->client);                        
  7. }
复制代码

early_suspend是sensor_private_data 里面的一个成员,通过这个成员的地址获取sensor_private_data结构体变量的地址,从而调用里面的成员变量client。
如何使用container_ofcontainer_of需要传入三个参数,第一个参数是一个指针,第二个参数是结构体类型,第三个是对应第二个参数里面的结构体里面的成员。
container_of(ptr, type, member)

           
  •         ptr:表示结构体中member的地址 h

           
  •         type:表示结构体类型 struct sensor_private_data

           
  •         member:表示结构体中的成员 early_suspend type里面一定要有这个成员,不能瞎搞啊

           
  •         返回结构体的首地址




一些知识({})、第一个先说这个表达式,很多人可能懂,可能在很多地方见到这个表达式,但是自己却没有注意,这个表达式返回最后一个表达式的值。比如x=({a;b;c;d;}),最终x的值应该是d。
typeof获取变量的类型这个我们很少看到,这个关键字是C语言关键字的拓展,返回变量的类型,具体可以看GCC里面的介绍 https://gcc.gnu.org/onlinedocs/gcc/Typeof.html
++Another way to refer to the type of an expression is with typeof. The syntax of using of this keyword looks like sizeof, but the construct acts semantically like a type name defined with typedef.++
代码例子:
  1. void main(void)
  2. {
  3.     int a = 6;
  4.     typeof(a) b =9;
  5.     printf("%d %d\n",a,b);
  6. }
复制代码

(struct st*)0的作用现在我们需要量一个结构体的长度,我们也可以用尺子来量,我们只要找到这个0刻度的位置就可以了。同理,即使我们不知道0刻度位置,我们首尾刻度相减一样可以计算出结构体的长度。 但是在C语言里什么是尺子呢?你想到的可能是sizeof,不幸的是,这个并不能满足我们的需要,所以才有了(struct st *),这个当作尺子真的再好不过了。
  1. struct st{
  2.     int a;
  3.     int b;
  4. }*p_st,n_st;

  5. void main(void)
  6. {
  7.     printf("%p\n",&((struct st*)0)->b);
  8. }
复制代码

上面的代码
(struct st*)0这个的意思就是把这个结构体放到0刻度上面开始量了,然后量到哪里呢?
&((struct st*)0)->b)这个就体现出来了,量到b的位置。所以上面的输出应该是4
看完上面的解释,应该知道下面这两个代码的功能是一样的。
typeof ((struct st*)0)->b) c; // 取b的类型来声明cint c;其实不只是对于0,用其他数字一样是有效的,比如下面的代码,编译器关心的是类型,而不在乎这个数字。
printf("%p\n",&((struct st*)4)->b  -4 );如果现在需要你把一个数组的首地址设置为0,要怎么做呢?
先思考一下,假设这里延迟了几分钟。
代码如下:
  1. struct A {
  2.     short array[100];
  3. };

  4. int main(int argc, char *argv[])
  5. {
  6.     int i = 10;

  7.     A* a = (A*)0;
  8.     printf("%p %d %d\n",a,sizeof(short), &a->array[20]);
  9.     getchar();
  10.     return 1;
  11. }
  12. //输出 00000000 2 40
复制代码

offsetof(TYPE, MEMBER)#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)size_t 这个有不懂的可以百度下,就是unsigned 的整数,在32位和64位下长度不同,所以这个offsetof就是获取结构体的偏移长度
const int* p的作用上面的宏定义里面还有一个小知识点
const typeof( ((type *)0)->member ) *__mptr上面的代码可以简写成
const int * __mptr这个说明什么问题呢?这个说明__mptr指向的整型数据是一个const(常数)。 这就涉及到两外两个知识
int * const __mptr;//表示__mptr的值不能改变//和const int * const __mptr; //表示__mptr不能改变而且指向的内容也不能改变container_of 剖析看完上面的几个知识点,再来看container_of这个宏就显得非常清晰了。我把解析部分写在下面的代码注释里面。
  1. <pre>#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)
  2. #define container_of(ptr, type, member) ({          \
  3.         const typeof( ((type *)0)->member ) *__mptr = (const typeof( ((type *)0)->member ) *)(ptr); \
  4.         (type *)( (char *)__mptr - offsetof(type,member) );})
  5. //-----分割线------
  6. struct st{
  7.     int a;
  8.     int b;
  9. }*pt;
  10. //用这个来举例
  11. container_of(&pt->a,struct st,a)
  12. const typeof( ((struct st *)0)->a ) *__mptr = (const typeof( ((struct st *)0)->a ) *)(&pt->a);
  13. const int *__mptr = (int *)(&pt->a);//第一句解析完,实际上就是获取a的地址。
  14. (type *)( (char *)__mptr - offsetof(type,member) );
  15. //这个变成
  16. (struct st *)( (char *)__mptr - ((unsigned int) &((struct st*)0)->a));
  17. //这句的意思,把a的地址减去a对结构体的偏移地址长度,那就是结构体的地址位置了。</pre>
  18. <span data-cke-copybin-end="1">​</span>
复制代码


回复

使用道具 举报

  • TA的每日心情
    开心
    2024-4-10 22:38
  • 签到天数: 1335 天

    [LV.10]以坛为家III

    88

    主题

    4292

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    9049
    最后登录
    2024-4-13
    发表于 2021-11-22 15:44:13 | 显示全部楼层
    当年看这部分内容的时候,突然觉得C语言的灵活性与变通性也是蛮强的。
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    656

    主题

    6312

    帖子

    0

    超级版主

    Rank: 8Rank: 8

    积分
    19944
    最后登录
    2024-4-19
     楼主| 发表于 2021-11-23 11:04:03 | 显示全部楼层
    jobszheng5 发表于 2021-11-22 15:44
    当年看这部分内容的时候,突然觉得C语言的灵活性与变通性也是蛮强的。

    内核才是学习C语言最好的地方
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-20 07:08 , Processed in 0.112761 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表