必威体育Betway必威体育官网
当前位置:首页 > IT技术

container_of函数

时间:2019-06-27 18:43:10来源:IT技术作者:seo实验室小编阅读:78次「手机版」
 

container_of

在linux 内核编程中,会经常见到一个宏函数container_of(ptr,type,member), 但是当你通过追踪源码时,像我们这样的一般人就会绝望了(这一堆都是什么呀? 函数还可以这样定义??? 怎么还有0呢??? 哎,算了,还是放弃吧。。。)。 这就是内核大佬们厉害的地方,随便两行代码就让我们怀疑人生,凡是都需要一个过程,慢慢来吧。

其实,原理很简单: 已知结构体type的成员member的地址ptr,求解结构体type的起始地址。

type的起始地址 = ptr - size (这里需要都转换为char *,因为它为单位字节)。

到此,该函数已经讲完,是不是很简单??? 其实也不是,这里并没有提到size如何计算,而令我们头晕的正是这里。

好吧,先上container of函数原型:

  1. #define container_of(ptr, type, member) ({ \

  2. const typeof( ((type *)0)->member ) *__mptr = (ptr); \

  3. (type *)( (char *)__mptr - offsetof(type,member) );})

其次为 offserof 函数原型:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

怎么样,是不是很炫? 好吧,下面开始揭开面纱:

(一)0 指针的使用 (自己给的名字,不知有木问题)

让事实说话:

  1. #include<stdio.h>

  2. struct test

  3. {

  4. char i ;

  5. int j;

  6. char k;

  7. };

  8. int main()

  9. {

  10. struct test temp;

  11. printf("&temp = %p\n",&temp);

  12. printf("&temp.k = %p\n",&temp.k);

  13. printf("&((struct test *)0)->k = %d\n",((int)&((struct test *)0)->k));

  14. }

编译运行,可以得到如下结果:

  1. &temp = 0xbf9815b4

  2. &temp.k = 0xbf9815bc

  3. &((struct test *)0)->k = 8

什么意思看到了吧,自定义的结构体有三个变量:i,j,k。 因为有字节对齐要求,所以该结构体大小为4bytes * 3 =12 bytes. 而&((struct test *)0)->k 的作用就是求 k到结构体temp起始地址的字节数大小(就是我们的size)。在这里0被强制转化为struct test *型, 它的作用就是作为指向该结构体起始地址的指针就是作为指向该结构体起始地址的指针就是作为指向该结构体起始地址的指针, 而&((struct test *)0)->k 的作用便是求k到该起始指针的字节数。。。其实是求相对地址,起始地址为0,则&k的值便是size大小(注:打印时因为需要整型,所以有个int强转)所以我们便可以求我们需要的 size 了 。 好吧,一不小心把 offsetof() 函数的功能给讲完了:::

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这次再看就顺眼了吧大笑(底层为什么是这样我还是不懂。。。只知道这样确实可以) , 所以offsetof()的作用就是求我们梦寐以求的size, 并以size_t形式返回(size_t: 无符号整型)。

(二) 内核编程的严谨性

  1. #define container_of(ptr, type, member) ({ \

  2. const typeof( ((type *)0)->member ) *__mptr = (ptr); \

  3. (type *)( (char *)__mptr - offsetof(type,member) );})

这里我们只看第二行:
const typeof( ((type *)0)->member ) *__mptr = (ptr);  

它的作用是什么呢? 其实没什么作用(尴尬尴尬尴尬勿喷勿喷,让我把话说完),但就形式而言 _mptr = ptr, 那为什么要要定义一个一样的变量呢??? 其实这正是内核人员的牛逼之处:如果开发者使用时输入的参数有问题:ptr与member类型不匹配,编译时便会有warnning, 但是如果去掉改行,那个就没有了,而这个警告恰恰是必须的(防止出错有不知道错误在哪里)。。。这严谨性可以吧抓狂抓狂抓狂

typeof( ((type *)0)->member )

它的作用是获取member的类型仅此而已。至此基本结束

(三) 总结

container_of(ptr, type,member)函数的实现包括两部分:

1. 判断ptr 与 member 是否为同意类型

2. 计算size大小,结构体的起始地址 = (type *)((char *)ptr - size) (注:强转为该结构体指针)

现在我们知道container_of()的作用就是通过一个结构变量中一个成员的地址找到这个结构体变量的首地址。

container_of(ptr,type,member),这里面有ptr,type,member分别代表指针、类型、成员。

            </p>

相关阅读

微分、积分、三角函数、数学公式大全

https://blog.csdn.net/nantongcjq/article/details/78987467 一、极限公式 (系数不为0的情况) 二、重要公式 三、下列常用等

GetDlgItem(函数详解)

hwndScroll = GetDlgItem(hwnd, IDC_SCROLL);假设一个父窗口中有多个子窗口。那么本函数是返回一个子窗口句柄。第一个参数:父窗口

scanf()函数用法规则探索——c语言,以及%c和%s的语法特性

一: 针对%c读入 从以上程序测得,对于%c 来说 scanf()接收键盘输入的所有东西,遇回车结束输入 ,可以读入空格,句号 当scanf以%c接收一

Linux下随机数生成的函数与常见方法

rand函数: 头文件#include<stdlib.h> 定义函数int rand(void) 函数说明rand()会返回一随机数值,范围在0至RAND_MAX 间。在调用此

matlab之length函数

1、size获取数组的行数和列数2、length数组长度,即行数和列数中的较大值,相当于max(size(a))3、numel返回元素总数

分享到:

栏目导航

推荐阅读

热门阅读