内核中container_of宏的详细分析【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637597.html
- 内核中container_of宏的详细分析
- 16年2月28日09:00:37
- 内核中有一个大名鼎鼎的宏-----container_of();这个宏定义如下所示,为了表示一下敬意,我就把注释一起粘贴下来了:
- /**
- * container_of - cast a member of a structure out to the containing structure
- * @ptr:the pointer to the member.
- * @type:the type of the container struct this is embedded in.
- * @member:the name of the member within the struct.
- *
- */
- #define container_of(ptr, type, member) ({\
- const typeof( ((type *)0)->member ) *__mptr = (ptr);\
- (type *)( (char *)__mptr - offsetof(type,member) );})
- 先来说这个宏的意义:它根据结构体中某成员变量的指针来求出指向整个结构体的指针。指针类型从结构体某成员变量类型转换为 该结构体类型。
- 比如先定义一个结构体:
- struct test {
- char name[20] ;
- char i;
- int j;
- };
- 假如,我们不小心知道了变量j的地址,那么我们想要通过j的地址来找到整个结构体test的地址,怎么来找呢???
- (一) offsetof宏:
- 结构体是一个线性存储的结构,无论在哪存放,j相对于整个结构体的地址的偏移值是不变的,于是,如果我们能够求出来这个偏移值的话,那么用j的地址减去这个偏移值不就是整个结构体的地址么~这是一个朴素的想法,内核中也确实这么做的~关键是怎么求出这个偏移值?内核的非常聪明的采取了下面的方法:
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
- (1)首先通过(TYPE *)0将0转换为TYPE类型的指针;
- (2)((TYPE *)0)->MEMBER 访问结构中的数据成员;
- (3)&(((TYPE *)0)->MEMBER)取出数据成员的地址;
- (4)(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型; 注意这里:这个&是取地址符号,不是按位与,注意运算符号的优先级。
- 巧妙之处在于将地址0强制类型转换成(TYPE*),结构体以内存空间首地址0作为起始地址,则各个结构体成员变量的偏移地址就等于其成员变量相对于整个结构体首地址的偏移量 。即:&(((TYPE *)0)->MEMBER)就是取出其成员变量的偏移地址,(size_t)(&(((TYPE*)0)->MEMBER))经过size_t的强制类型转换以后,其数值为结构体内的偏移量。
- 需要明确的一点是,地址就是地址,它没有类型之分,你把它强制转换成什么类型它就是什么类型,所以在c语言中有各种强制类型转换。
- 用下面的例子来说明:
- #include <stdio.h>
- struct test {
- char i;
- int j;
- int k;
- };
- int main(int argc, char const *argv[])
- {
- struct test *temp = 0;
- printf("%p \n", &(temp->j));
- printf("%d \n", (size_t) &(temp->j));
- printf("%p \n", &(temp->k));
- printf("%d \n", (size_t) &(temp->k));
- return 0;
- }
- 运行结果是:
- 0x4
- 4
- 0x8
- 8
- 可以看出来,通过采用这种方式,就可以求出来结构体中成员变量相对与整个结构体首地址的偏移量。
- (二) container_of宏
- 如果理解了上面的部分,再看这个container_of宏就不是那么难了,我们先想想它怎么实现:
- 假设我们知道一个test类型的结构体里面的一个成员变量j的地址,那么需要先求出这个j变量相对于整个结构体地址的偏移值,然后用这个j的地址减去这个偏移值就行了。但是还有一点,这个j变量的数据类型是什么样的?这个虽然我们知道test数据类型,但是我们怎么取出来j对应的数据类型呢?这时候就用到typeof关键字了,typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的数据类型。
- 下面来看这些代码:
- #define container_of(ptr, type, member) ({\
- const typeof( ((type *)0)->member ) *__mptr = (ptr);\
- (type *)( (char *)__mptr - offsetof(type,member) );})
- 首先
- (1)((type *)0)->member为设计一个type类型的结构体,并且这个结构体的的起始地址为0,然后将它指向我们知道的member变量,然后通过typeof( ((type *)0)->member )来获得member对应的数据类型。
- (2)const typeof( ((type *)0)->member ) *__mptr = (ptr);意思是声明一个与member同一个类型的指针常量 *__mptr,并初 始化为ptr.
- (3)(char *)__mptr - offsetof(type,member)意思是__mptr的地址减去member在该struct中的偏移量得到的地 址, 这样得到的就是整个结构体的首地址。
- (4)得到首地址后还没有完,上面说了,地址只是一个地址,它没有数据类型,所以最后再进行一一次强制类型转换,转换成我们需要的type类型的,即:
- (type *)( (char *)__mptr - offsetof(type,member) );
- (5)({ })这个扩展返回程序块中最后一个表达式的值。注意这个 container_of宏是两个表达式语句的综合。 相当与顺序执行了两个语句,这时候得到的地址就是member成员所在结构体的首地址。
- 要注意的是代码高亮处 ,(char *)__mptr 的作用是将__mptr 强制转换为字符指针类型,必须的!!!如果__mptr为整形指针 __mptr - offset 相当于减去sizeof(int)*offset个字节!!!
- 下面再看一个程序来温习一下:
- #include <stdio.h>
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
- #define container_of(ptr, type, member) ({\
- const typeof( ((type *)0)->member ) *__mptr = (ptr);\
- (type *)( (char *)__mptr - offsetof(type,member) );})
- struct test {
- char name[20] ;
- char i;
- int j;
- };
- int main(int argc, char const *argv[])
- {
- struct test temp = {"zer0", 'a', 26};
- printf("&temp = %p.\n", &temp);
- printf("&temp.i = %p.\n", &temp.i);
- printf("&temp.j = %p.\n", &temp.j);
- printf("offset of i = %d.\n", offsetof(struct test, i));
- printf("offset of j = %d.\n", offsetof(struct test, j));
- printf("&temp = %p.\n", container_of(&temp.i, struct test, i));
- printf("&temp = %p.\n", container_of(&temp.j, struct test, j));
- struct test *tmp = container_of(&temp.i, struct test, i);
- printf("tmp->name : %s, tmp->i : %c, tmp->j : %d.\n", tmp->name, tmp->i, tmp->j);
- return 0;
- }
- 运行结果如下所示:
- &temp = 0xbf8b41b0.
- &temp.i = 0xbf8b41c4.
- &temp.j = 0xbf8b41c8.
- offset of i = 20.
- offset of j = 24.
- &temp = 0xbf8b41b0.
- &temp = 0xbf8b41b0.
- tmp->name : zer0, tmp->i : a, tmp->j : 26.
转载于:https://www.cnblogs.com/sky-heaven/p/9597183.html
内核中container_of宏的详细分析【转】相关推荐
- Linux内核中container_of宏的理解
对 typeof 的理解: 实际上, typeof 并不是宏定义,它是GCC的关键字,是GCC特有的特性.如果只知道一个变量的名字要得到其类型,并不是宏定义能够完成的,这需要编译时的信息.所以,typ ...
- Linux内核中max()宏的奥妙何在?(二)——大神Linus对这个宏怎么看?
最新max()宏 上回,我们在<Linux内核中max()宏的奥妙何在?(一)>一文中说到,在3.18.34版Linux内核源码中的max()宏,采用了GCC的扩展特性,可以避免一些错误. ...
- Linux 内核中的宏定义
Linux 内核中的宏定义 rtoax 日期 内核版本:linux-5.10.13 注释版代码:https://github.com/Rtoax/linux-5.10.13 __attribute__ ...
- Linux内核中max()宏的奥妙何在?(一)
Linux内核中max()宏的奥妙何在?(一) 1.max()宏那点事 在Linux内核中,有这样四个比较大小的函数,如下: max(x,y) //两个数求最大值 min(x,y) //两个数求最小值 ...
- CE5.0 - eboot汇编Startup.s中MMU设置流程详细分析
CE5.0 - eboot汇编Startup.s中MMU设置流程详细分析 以下为SMDK开发板startup.s部分启动代码. ;------------------------------- ...
- Linux字符设备驱动中container_of宏的作用
Linux字符设备驱动中container_of宏的作用 首先看看这个宏的原型: container_of(ptr,type,member) 功能:根据一个结构体变量中的一个成员变量的指针来获取指向整 ...
- linux内核函数 ffs,linux内核中的宏ffs(x)【转】
linux内核中ffs(x)宏是平台相关的宏,在arm平台,该宏定义在 arch/arm/include/asm/bitops.h #define ffs(x) ({ unsigned long __ ...
- linux内核函数 ffs,linux内核中的宏ffs(x)
转自:https://www.cnblogs.com/fengeryi/p/3449720.html linux内核中ffs(x)宏是平台相关的宏,在arm平台,该宏定义在 arch/arm/incl ...
- Linux内核中断系统处理机制-详细分析
原文地址::https://blog.csdn.net/weixin_42092278/article/details/81989449 相关文章 1.Linux中断管理 (1)Linux中断管理机制 ...
最新文章
- 任务一,转换学习思路
- 关于主机的思维导图_几张思维导图,让你清楚的知道ip地址怎么回事?
- Qt C++ 检测优盘插入或拔出
- ABAP function module 的使用
- Java记录 -6- 流程控制语句
- 您对无法重新创建的表进行了更改或者启用了“阻止保存要求重新创建表的更改”选项...
- 微软想证明Windows比Chrome好 主要源自恐惧?
- 用javascript实现以下功能!_用python80行代码实现一个微信消息撤回捕捉功能
- 虚拟机使用本地服务器配置,虚拟机搭建本地云服务器配置
- 计量经济学计算机答案第三章课后答案,庞皓计量经济学第2版课后习题答案
- 元宵节代码,元宵节快乐代码,元宵节祝福代码
- Hexo-Fluid主题添加音乐页面
- 中国人的英文名和外国人的中文名
- python计算长方体体积代码_编写程序,计算一个长方体的表面积和体积。
- 模拟电子技术(一)半导体二极管和三极管
- ros操作命令与实操-话题发布
- java——设计模式
- Volatility内存分析工具 - 某即时通讯软件Windows端数据库密钥的分析
- 在使用matrix toolkits Java(mtj)矩阵工具包出现问题及解决
- python生成矩阵 元素随机_用python生成随机矩阵