关于Linux/kernel.h中的offsetof和container_of宏
一、
位于Linux/kernel.h中
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
简单应用:
struct A_t
{
char a;
char b;
int *c;
};
struct B_t
{
char a;
int *c;
};
struct A_t A;
struct B_t B;
struct A_t *A_add;
A.c = (int *)malloc(sizeof(int),1);
B.c = &A.c;
dev = container_of(inode->i_cdev, struct scullc_dev, cdev);
A_add = container_of(B.c,struct A_t,c);
二、offsetof宏
(s *)0 : 这里的用法实际上是欺骗了编译器,使编译器认为 "0" 就是一个指向 s 结构体的指针(地址),还句话说 s 结构体就是位于 0x0 这个地址处。
(s *)0-> m : 自然就是指向这个结构体的 m成员。
&((s *)0)->m : 表示 m成员的地址。这里,如上面所说,因为编译器认为结构体 s 被认为是处于 0x0 地址处,所以 m 的地址自然的就是 m 在 s 中的偏移地址了。
最后将这个偏移值转化为 size_t 类型。
可能会感到迷惑,这样强制转换后的结构指针怎么可以用来访问结构体字段?呵呵,其实这个表达式根本没有也不打算访问m字段。ANSIC标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((s*)0)的结果就是一个类型为s*的NULL指针。如果利用这个NULL指针来访问s的成员当然是非法的,但&(((s*)0)->m)的意图并非想存取s字段内容,而仅仅是计算当结构体实例的首址为((s*)0)时m字段的地址。聪明的编译器根本就不生成访问m的代码,而仅仅是根据s的内存布局和结构体实例首址在编译期计算这个(常量)地址,这样就完全避免了通过NULL指针访问内存的问题。又因为首址的值为0,所以这个地址的值就是字段相对于结构体基址的偏移。
size_t是针对系统定制的一种数据类型。它不是固定位数,在不同的系统里这个值都有可能不同( 它实际上是 unsigned int 类型 );而且在内存里,对于数据是高位对齐存储还是低位对齐存储各系统都不一样。所以,为了提高代码的可移植性,就有必要定议这样的数据类型。一般这种类型都会定义到它具体占几位内存等。当然,有些是编译器或系统已经给定义好的。
#ifndef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
三、container_of宏
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
功能是计算返回包含ptr指向的变量所在的type类型结构变量的指针。(比较拗口)
该宏的实现思路:计算type结构体成员member在结构体中的偏移量,然后ptr的地址减去这个偏移量,就得出type结构变量的首地址。
该宏的实现方法:
2、用宏offsetof(type,member),获取member成员在type结构中的偏移量
3、最后将__mptr值减去这个偏移量,就得到这个结构变量的地址了(亦指针)。
typeof是个关键字,可用来引用宏参数的类型。
示例
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- struct test{
- int a;
- char b;
- int c;
- };
- struct test tmp = {
- .a = 10,
- .b = 20,
- .c = 30
- };
- int *val = &tmp.b;
- static __init int init_func(void)
- {
- struct test *tst;
- tst = container_of( val, struct test, b );
- printk(KERN_DEBUG"a = %d, b = %d, c = %d\n",tst->a,tst->b,tst->c);
- return 0;
- }
- static __exit void release_func(void)
- {
- printk(KERN_DEBUG"88\n");
- }
- module_init(init_func);
- module_exit(release_func);
参考文章:
[1].Linux/kernel.h中强大的container_of宏
[2].offsetof 详解
[3]. 对container_of(ptr,type,member)分析
关于Linux/kernel.h中的offsetof和container_of宏相关推荐
- (六)linux内核中的offsetof与container_of宏
参考: offsetof与container_of宏[总结] #define offsetof(type, member) (size_t)&(((type*)0)->member)#d ...
- Kprobe在Linux kernel debug中的应用
一直在做kernel开发方面的工作,也一直苦于kernel debug的困惑,到底如何进行kernel开发的debug的工作?今天经美国同事的推荐,我认为kprobe是一个非常好的debug工具.其本 ...
- Linux内核代码宏定义,Linux Kernel源代码中与段有关的重要宏定义
__init, __initdata等属性标志,是要把这种属性的代码放入目标文件的.init.text节,数据放入.init.data节──这一过程是通过编译内核时为相关目标平台提供了xxx.lds链 ...
- 如何在Linux kernel Makefile中添加宏定义
如何在Linux kernel Makefile中添加宏定义: CFLAGS_object.o += -DMACRO_NAME 在编译object.o时定义宏MACRO_NAME,在kernel中添加 ...
- Linux内核 sched,struct sched_domain在include / linux / sched.h中的含义(在内核中调度域)...
我试图了解负载均衡器如何在Linux内核中的多处理器系统上运行, Linux调度程序基本上使用runques来存储它必须在下一次运行的任务,现在以执行load_balancer()的方式处理多处理器系 ...
- linux中offsetof与container_of宏定义
linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->M ...
- linux/init.h 中__init
原始定义:include/linux/init.h __init和__exit标记函数,__initdata和__exitdata标记数据. 此宏定义可知标记后的函数与数据其实是放到了特定的(代码或数 ...
- linux kernel 学习中, #define ICPU(model){ X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } 的用法
define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } 可以当做是自定义的数据模型,可以当做数组但是不能用数组方式调用, ...
- 转:linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析
在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个 ...
- linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析
在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个 ...
最新文章
- Linux各个发行版本的选择
- JS仿淘宝关闭二维码案例
- 听说过OpenJDK,没说过OpenValueJDK吧?
- keras 以图搜图
- python经典算法题:无重复字符的最长子串
- vscode html如何插入模板?(!)
- myeclipse部署ssh项目工程
- python中怎么取整数案例题_python中如何取整数
- 心有所鼠,鼠年快乐~
- jquery视频教程(jquery视频教程全集)
- sqlServer相关
- 创建Vue实例对象基础语法模板
- 设置Mysql数据库的默认编码为utf8
- 你绝没见过的奢华 全球最贵的13样东西
- MySQL事务隔离与行锁的关系
- linux服务器安装zip解压缩工具
- Linux学习-66-系统日志管理
- CIELab图像的通道分解与合成
- P2669 金币,国王将金币作为工资,发放给忠诚的骑士。第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十
- 讲解c语言算法的视频,c语言算法学习视频