剖析linux内核中的宏---------container_of
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
#endif
作用:通过结构体成员变量member的地址,反推出member成员所在结构体变量的首地址,ptr指向成员变量member。
解析:
1)({ })是何方神圣?
({ })是GNU C编译器的语法扩展
({ })与逗号表达式类似,结果作为最后一个语句的值

2)typeof是一个关键字吗?
typeof是GNU C编译器的特有的关键字
typeof只在编译期生效,用于得到变量的类型
3)最后的原理

首先通过offsetof计算出成员变量c在结构体中的偏移量;
pc是指向结构体中成员变量c的指针;
(char *)pc 将pc指针强制类型转换为char *,目的就是为了做指针运算
所实现的就是通过结构体中的一个成员变量的地址反推结构体变量的首地址。
现通过代码来说明,

结构体变量s的值就是结构体变量的首地址,通过container_of也可以得到结构体变量的首地址,两者打印出来的值是相同的。
细心的同学可能会发现,container_of宏中的第二行代码有什么用呢,现在我们就用代码说明,它到底有何用处?

我们将宏中的第二行代码删掉,运行结果同样正确,你心中是否会这样想:这行代码完全可以删掉,因为它对运行结果没有什么影响。如果你这么想,那么请继续往下看:
首先使用我们修改的这个宏,即将第二行代码删掉:

编译过后仅仅告诉我们pc指针没有使用,然后接下来运行。

从中可以看出能够正常的运行,但是运行结果不是我们想要的结果,是不对的。
现在我们用内核提供给我们的宏,即没有经过修改的宏:

在进行编译的时候,多出现了一个警告,提示我们类型不兼容。
所以container_of宏中的第二行代码,是为了做类型检查的。
container_of的功能只能用宏来实现,宏其实是由预处理器在编译的时候来进行处理的。预处理器做的是单纯的文本替换,不会进行任何的类型检查。这就有可能导致我们在编写代码的时候由粗心大意而造成的错误,就像上面的这个错误误用了pi指针。这时候,为了增加代码的安全性,为了有一点点的类型检查,所以在linux内核中该宏的定义中加上了const typeof(((type *)0)->member) * __mptr = (ptr);这条语句。
可能还有疑问?
1)不使用({}),使用逗号表达式能否实现 不可以,因为里面有指针的定义,不能存在于逗号表达式中
2)(type *)0)->member 访问了0地址,会导致程序的崩溃吗?
typeof是在编译期有效,在编译期间就可以拿到成员变量的类型了,不用等到运行期间了。在运行的时候,该条语句就不存在了,因此不会导致程序的崩溃。
剖析linux内核中的宏---------container_of的更多相关文章
- 剖析linux内核中的宏-----------offsetof
offsetof用于计算TYPE结构体中MEMBER成员的偏移位置. #ifndef offsetof#define offsetof(TYPE, MEMBER) ((size_t) &((T ...
- linux内核中的宏ffs(x)
linux内核中ffs(x)宏是平台相关的宏,在arm平台,该宏定义在 arch/arm/include/asm/bitops.h #define ffs(x) ({ unsigned long __ ...
- KSM剖析——Linux 内核中的内存去耦合
简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging) 允许这个系统管理程序通 ...
- Linux内核中的宏:__init and __exit
ZZ FROM: http://blog.csdn.net/musein/article/details/742609 ======================================== ...
- (十)Linux内核中的常用宏container_of
Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...
- Linux内核中的常用宏container_of
Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Containe ...
- Linux内核中的常用宏container_of其实很简单【转】
转自:http://blog.csdn.net/npy_lp/article/details/7010752 开发平台:Ubuntu11.04 编 译器:gcc version 4.5.2 (Ubun ...
- Linux内核中的fastcall和asmlinkage宏
代码中看见:#define _fastcall 所以了解下fastcall -------------------------------------------------------------- ...
- Linux内核中SPI/I2c子系统剖析
Linux内核中,SPI和I2C两个子系统的软件架构是一致的,且Linux内核的驱动模型都以bus,driver,device三种抽象对象为基本元素构建起来.下文的分析将主要用这三种抽象对象的创建过程 ...
随机推荐
- Paper | Predicting the Quality of Images Compressed After Distortion in Two Steps
目录 1. 问题本质剖析 2. 方法细节 图像质量评估大佬AC Bovik的论文,发表在2019 TIP上. 考虑的问题:对于有参考图像质量评估(R-IQA)任务,参考图像有时是有损的.这会导致评估的 ...
- Debug 路漫漫-15:Python: NameError:name 'dataset' is not defined
在调试 <Outer Product-based Neural Collaborative Filtering>论文的源码(https://github.com/duxy-me/ConvN ...
- 队列和 BFS —— 栈和 DFS
队列和 BFS: 广度优先搜索(BFS)的一个常见应用是找出从根结点到目标结点的最短路径. 示例 这里我们提供一个示例来说明如何使用 BFS 来找出根结点 A 和目标结点 G 之间的最短路径. 洞悉 ...
- QuantLib 金融计算——自己动手封装 Python 接口(1)
目录 QuantLib 金融计算--自己动手封装 Python 接口(1) 概述 QuantLib 如何封装 Python 接口? 自己封装 Python 接口 封装 Array 和 Matrix 类 ...
- laravel报错:SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '0' for key 'PRIMARY' (SQL: insert into `cart` (`uid`, `gid`, `gname`, `price`) values (3, 21, 夏季日系复古工装短袖衬衫男士印花潮流宽松五分
原因:要操作的数据表id没有设置自增,导致出现id为0的情况 解决方法:给该数据表的id字段设置自增
- poj-2935 BFS Basic Wall Maze
Basic Wall Maze Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 3384 Accepted: 1525 ...
- python asyncio call_soon, call_at, call_later
1. call_soon, 协程一运行就马上运行 def callback(sleep_times): print("success time {}".format(sleep_t ...
- MongoDB官方下载安装设置配置文件指定端口号
1.)下载 官网(https://www.mongodb.com/)右上角try free 进入下载中心,下载指定版本 ZIP和MSI随便 如果浏览器下载的慢,可以直接使用下载地址,然后迅雷下 操作 ...
- .NET工程师的书单
短暂的假期里抽空整理了一份书单,以个人的见解这些应该是值得.NET工程师至少去看一遍的书籍.但所罗列的仅包括国内目前已出版的国外书籍的英文版,并不包含中文翻译及相关领域的中文书籍.这里没有任何歧视之意 ...
- GAN简介
GAN Generative Adversarial Networks 生成对抗网络.学习真实世界的真实数据的分布,用于创造以假乱真的数据.比如前段时间很火的应用deep fake.deep nude ...