剖析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三种抽象对象为基本元素构建起来.下文的分析将主要用这三种抽象对象的创建过程 ...
随机推荐
- base46+url编码
base64解密+unquote url编码 html = ''' JTdCJTIyc3RvcmUlMjIlM0ElN0IlMjJnYW1lTGlicmFyeSUyMiUzQSU3QiUyMmRpc2 ...
- python 生成sql语句
1. 别名 s = '' name = ['张三', '李四', '王五'] for i in range(len(name)): s += "'" + name[i] + &qu ...
- Django2.2报错 django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.
准备将 Django 连接到 MySQL,在命令行输入命令 python manage.py makemigrations 后报错: django.core.exceptions.Improperly ...
- 《Java多线程设计模式》学习
还是别人的笔记比较详细: https://segmentfault.com/blog/ressmix_multithread?page=3 mark.
- 其它 用VB6创建ActiveX.dll
1.打开VB6 2.选择 ActiveX DLL,点击打开 3.在窗口输入测试代码 Public Function addstr(str As String) As String addstr = & ...
- Loj #2719. 「NOI2018」冒泡排序
Loj #2719. 「NOI2018」冒泡排序 题目描述 最近,小 S 对冒泡排序产生了浓厚的兴趣.为了问题简单,小 S 只研究对 *\(1\) 到 \(n\) 的排列*的冒泡排序. 下面是对冒泡排 ...
- python-3-条件判断练习题
前言 我们在前面两章学习了基础数据类型与条件判断语句,今天我们来做下练习题.如果你有不一样的解题思路在评论区亮出你的宝剑!!! 一.习题如下: 1.使用 while 循环输出 1 2 3 4 5 6 ...
- Wamp 下运行 CGI 笔记
虽然假期的余额不足了,但是仔细想想放假又有多少事情可以做呢?休息的差不多了,还是上班的好,长时间的休息人就废了.同意的举手,不同意的就算了. httpd.conf 的配置 我这里使用的是 Wamp 的 ...
- .net core EF Core 调用存储过程
在这里,我们将尝试去学习一下 .net core EF Core 中调用存储过程. 我们知道,EF Core 是不支持直接调用存储过程的,那它又提供了什么样的方式去执行存储过程呢?有如下方法: 1.F ...
- Java使用路径通配符加载Resource与profiles配置使用
序言 Spring提供了一种强大的Ant模式通配符匹配,能从一个路径匹配一批资源. Ant路径通配符 Ant路径通配符支持“?”.“*”.“**”,注意通配符匹配不包括目录分隔符“/”: “?”:匹配 ...