[系统技巧] Linux 内核 container_of 宏详解
作者:CC下载站 日期:2023-10-11 23:50:37 浏览:49 分类:玩电脑
前言
1、container_of 宏介绍
到这里假设大家都懂了 typeof 和 语句表达式,那么我们就开始一睹 Linux 内核第一宏 container_of 的芳容吧:
#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) );})
作为 Linux 内核第一个宏,绝对是实至名归的,看看它外表斯文而内藏八块腹肌的身形,就知道它是不好惹的。宏中有宏,作为 GNU C 高端扩展特性的综合运用,那么它有什么作用呢?它的主要作用是:根据结构体某一成员的地址,获取这个结构体的首地址。根据宏定义,可知这个宏有三个参数:
type:结构体类型
member:结构体内的成员
ptr:结构体内成员 member 的地址
也就是说,当我们知道了一个结构体的类型,结构体内某一成员的地址,也就可以直接获得到这个结构体的首地址。container_of 宏返回的就是这个结构体的首地址。
2、container_of 宏的使用示例
这个宏在内核中非常重要。在内核中会经常有这样的需求:我们传递给某个函数的参数是某个结构体的成员变量,然后在这个函数中,可能还会用到此结构体的其它成员变量,那么这个时候怎么办呢?我们可以使用 container_of 先通过结构体某一成员的访问找到这个结构体的首地址,然后就可以访问其它成员变量了。
struct _box_t { double length; // 盒子的长度 double breadth; // 盒子的宽度 double height; // 盒子的高度 }; int main(void) { struct _box_t box = {30.0, 20.0, 10.0}; struct _box_t *p_box = NULL; p_box = container_of(&box.height, struct _box_t, height); printf("%p\n", p_box); printf("length: %f\n", p_box->length); printf("breadth: %f\n", p_box->breadth); return 0; }
在这个程序中,我们定义一个结构体变量 box,知道了它的成员变量 height 的地址 &box.height,就可以通过 container_of 宏直接获得 box 结构体变量的首地址,然后直接访问 box 结构体的其它成员 p_box->length 和 p_box->breadth。
3、container_of 宏实现原理分析
container_of 宏的实现主要用到的知识为:语句表达式和 typeof,再加上结构体存储的基础知识。为了帮助大家更好地理解这个宏,我们先复习下结构体存储的基础知识。
3.1 结构体在内存中的存储
我们知道,结构体作为一个复合类型数据,它里面可以有多个成员。当我们定义一个结构体变量时,编译器要给这个变量在内存中分配存储空间。除了考虑数据类型、字节对齐等因素之外,编译器会按照结构体中各个成员的顺序,在内存中分配一片连续的空间来存储它们。
struct _box_t { double length; // 盒子的长度 double breadth; // 盒子的宽度 double height; // 盒子的高度 }; int main(void) { struct _box_t box = {30.0, 20.0, 10.0}; printf("&box = %p\n", &box); printf("&box.length = %p\n", &box.length); printf("&box.breadth = %p\n", &box.breadth); printf("&box.height = %p\n", &box.height); return 0; }
在这个程序中,我们定义一个结构体,里面有三个 double 型数据成员,我们定义一个变量,然后分别打印结构体的地址、各个成员变量的地址,运行结果如下:
&box = 2b6c3dd0 &box.length = 2b6c3dd0 &box.breadth = 2b6c3dd8 &box.height = 2b6c3de0
从运行结果我们可以看到,结构体中的每个成员变量,从结构体首地址开始,依次存放。每个成员变量相对于结构体首地址,都有一个固定偏移。比如 breadth 相对于结构体首地址偏移了8个字节。height 的存储地址,相对于结构体首地址偏移了16个字节。
3.2 计算成员变量在结构体内的偏移
一个结构体数据类型,在同一个编译环境下,各个成员相对于结构体首地址的偏移是固定的。我们可以修改一下上面的程序,当结构体的首地址为 0 时,结构体中的各成员地址在数值上等于结构体各成员相对于结构体首地址的偏移。
struct _box_t { double length; // 盒子的长度 double breadth; // 盒子的宽度 double height; // 盒子的高度 }; int main(void) { printf("&length = %p\n", &((struct _box_t*)0)->length); printf("&breadth = %p\n", &((struct _box_t*)0)->breadth); printf("&height = %p\n", &((struct _box_t*)0)->height); return 0; }
在上面的程序中,我们没有直接定义结构体变量,而是将数字 0 通过强制类型转换,转换为一个指向结构体类型为 _box_t 的常量指针,然后分别打印这个常量指针指向的结构体的各成员地址。运行结果如下:
&length = ox0 &breadth = 0x8 &height = 0x10
因为常量指针为 0,即可以看做结构体首地址为 0,所以结构体中每个成员变量的地址即为该成员相对于结构体首地址的偏移。container_of 宏的实现就是使用这个技巧来实现的。
3.3 container_of 宏的原理实现
container_of 宏整体的实现原理如图所示:
从语法角度来看,container_of 宏的实现由一个语句表达式构成:
#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) );})
语句表达式的值即为最后一个表达式的值:
(type *)( (char *)__mptr - offsetof(type,member) );
以上这个语句的意义就是,拿结构体某个成员 member 的地址,减去这个成员在结构体 type 中的偏移,结果就是结构体 type 的首地址。因为语句表达式的值等于最后一个表达式的值,所以这个结果也是整个语句表达式的值,container_of 最后就会返回这个地址值给宏的调用者。
内核中定义了 offset 宏来计算结构体某个成员在结构体内的偏移,它的定义如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
这个宏有两个参数,一个是结构体类型 TYPE,一个是结构体的成员 MEMBER,它使用的技巧跟我们上面计算 0 地址常量指针的偏移是一样的:将 0 强制转换为一个指向 TYPE 的结构体常量指针,然后通过这个常量指针访问成员,获取成员 MEMBER 的地址,其大小在数值上就等于 MEMBER 在结构体 TYPE 中的偏移。
因为结构体的成员数据类型可以是任意数据类型,所以为了让这个宏兼容各种数据类型。我们定义了一个临时指针变量 __mptr ,该变量用来存储结构体成员 MEMBER 的地址,即存储 ptr 的值。那么如何获取 ptr 指针类型呢?通过下面的方式:
typeof( ((type *)0)->member ) *__mptr = (ptr);
以上宏的参数 ptr 代表的是一个结构体成员变量 MEMBER 的地址,所以 ptr 的类型是一个指向 MEMBER 数据类型的指针。为了确保临时变量 __mptr 的指针类型也是一个指向 MEMBER 类型的指针变量,通过 typeof( ((type *)0)->member ) 表达式,使用 typeof 关键字来获取结构体成员 member 的数据类型,然后使用 typeof( ((type *)0)->member ) *__mptr 就可以定义一个指向该类型的指针变量了。
注意:在语句表达式的最后,因为返回的是结构体的首地址,所以数据类型还必须强制转换为 TYPE *,即返回一个指向 TYPE 结构体类型的指针,所以你会在最后一个表达的 offset 宏 中看到一个强制类型转换(TYPE *)。
4、总结
通过对 container_of 宏的整体分析后,这个过程到底对我们有什么启发呢?
对于任何一个复杂的技术,我们都可以把它由上而下的逐步分解,然后运用所学的基础知识一点一点剖析:先进行小模块分析,然后再进行综合分析。
比如 container_of 宏的定义,就运用了结构体的存储、语句表达式、typeof 等知识点。
当我们掌握了这些基础知识,并且有了分析方法,以后在内核中再遇到这样类似的宏,我们就可以自信从容地去自己分析,而不必总是依赖网上大海捞针式的搜索了。
这就是你的核心竞争力,也是你超越其他工程师、脱颖而出的机会。
猜你还喜欢
- 09-06 [系统技巧] windows中,端口查看 关闭进程及Kill使用
- 09-06 [系统教程] Windows系统Git安装教程(详解Git安装过程)
- 06-26 [系统技巧] 远程桌面连接(mstsc)全攻略
- 03-29 [玩系统] Windows下强大的命令终端-Cmder
- 03-29 [系统技巧] Win10右键菜单添加“获取文件管理员权限”选项
- 03-29 [系统技巧] win10删除文件夹需要Administrator权限
- 03-09 [网络技巧] 如何使用IPv6地址直接访问http,https服务及Windows共享文件夹[UNC路径]
- 11-05 [系统技巧] 真的有后悔药!Win10超给力的备份功能你用过吗
- 11-05 [系统技巧] 发现Win11、Win10备份已禁用?帮你快速解决!
- 11-05 [电脑技巧] 科普|F1-F12快捷键有什么用?
- 11-05 [系统技巧] 永劫无间游戏卡顿、闪退 只需检查电脑这个设置
- 11-05 [系统技巧] Win10出现Runtime Error如何修复
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[课程] 《大师级航拍教程》63节课程视频 MP4格式 5.9G
[资料] 中医鬼才倪海厦全集完整版+资料全集
[课程] 聂佳判断推理绝版课程大集合【8G】
[电视剧] 芈月传 【全集81集全】【未删减版】【国语中字】【2015】【HD720P】【75G】
[电视剧] 封神榜 梁丽版 (1989) 共5集 480P国语无字 最贴近原著的一版【0.98 G】
[影视] 【雪山飞孤4个版本】【1985、1991、1999、2007】【1080P、720P】【中文字幕】【167.1G】
[资料] 24秋初中改版教材全集(全版本)[PDF]
[电影] 高分国剧《康熙王朝》(2001)4K 2160P 国语中字 全46集 78.2G
[动画] 迪士尼系列动画139部 国英双语音轨 【蓝光珍藏版440GB】
[电影] 莫妮卡贝鲁奇为艺术献身电影大合集 1080P超清 双语字幕
[书籍] 彭子益医书合集 [PDF/DOC]
[游戏] 《黑神话悟空》免安装学习版【全dlc整合完整版】+Steam游戏解锁+游戏修改工具!
[动画] 《名侦探柯南》名侦探柯南百万美元的五菱星 [TC] [MP4]
[动画] 2002《火影忍者》720集全【4K典藏版】+11部剧场版+OVA+漫画 内嵌简日字幕
[剧集] 《斯巴达克斯》1-4季合集 无删减版 1080P 内嵌简英特效字幕
[CG剧情] 《黑神话:悟空》158分钟CG完整剧情合集 4K120帧最高画质
[游戏] 黑神话悟空离线完整版+修改器
[电影] 《变形金刚系列》七部合集 [4K HDR 蓝光] 国英双语音轨 [内封精品特效字幕]【典藏版】235G
[图像处理] 光影魔术手v4.6.0.578绿色版
[动画] 西游记 (1999) 动画版 4K 全52集 高清修复版 童年回忆
[影视] 美国内战 4K蓝光原盘下载+高清MKV版/内战/帝国浩劫:美国内战(台)/美帝崩裂(港) 2024 Civil War 63.86G
[影视] 一命 3D 蓝光高清MKV版/切腹 / 切腹:武士之死 / Hara-Kiri: Death of a Samurai / Ichimei 2011 一命 13.6G
[影视] 爱情我你他 蓝光原盘下载+高清MKV版/你、我、他她他 2005 Me and You and Everyone We Know 23.2G
[影视] 穿越美国 蓝光原盘下载+高清MKV版/窈窕老爸 / 寻找他妈…的故事 2005 Transamerica 20.8G
[电影] 《黄飞鸿》全系列合集
[Android] 开罗游戏 ▎像素风格的模拟经营的游戏厂商安卓游戏大合集
[游戏合集] 要战便战 v0.9.107 免安装绿色中文版
[书籍] 彭子益医书合集 [PDF/DOC]
[资源] 精整2023年知识星球付费文合集136篇【PDF格式】
[系统]【黑果小兵】macOS Big Sur 11.0.1 20B50 正式版 with Clover 5126 黑苹果系统镜像下载
- 最新评论
-
找了好久的资源bjzchzch12 评论于:11-07 谢谢分享感谢ppy2016 评论于:11-05 谢谢分享感谢ppy2016 评论于:11-05 怎么没有后续闲仙麟 评论于:11-03 怎么没后续闲仙麟 评论于:11-03 有靳东!嘻嘻奥古斯都.凯撒 评论于:10-28 流星花园是F4处女作也是4人集体搭配的唯一一部!奥古斯都.凯撒 评论于:10-28 找了好久的资源,终于在这里找到了。感谢本站的资源和分享。谢谢AAAAA 评论于:10-26 找了好久的资源,终于在这里找到了。感谢本站的资源和分享。谢谢password63 评论于:10-26 找了好久的资源,终于在这里找齐了!!!!blog001 评论于:10-21
- 热门tag