在linux 内核编程中,会经常见到一个宏函数container_of(ptr,type,member), 但是当你通过追踪源码时,像我们这样的一般人就会绝望了(这一堆都是什么呀? 函数还可以这样定义??? 怎么还有0呢???  哎,算了,还是放弃吧。。。)。 这就是内核大佬们厉害的地方,随便两行代码就让我们怀疑人生,凡是都需要一个过程,慢慢来吧。

其实,原理很简单:  已知结构体type的成员member的地址ptr,求解结构体type的起始地址。

type的起始地址 = ptr - size      (这里需要都转换为char *,因为它为单位字节)。

到此,该函数已经讲完,是不是很简单??? 其实也不是,这里并没有提到size如何计算,而令我们头晕的正是这里。

好吧,先上container of函数原型:

#define container_of(ptr, type, member) ({              \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

其次为 offserof 函数原型:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

怎么样,是不是很炫?  好吧,下面开始揭开面纱:

  (一)0 指针的使用    (自己给的名字,不知有木问题)

让事实说话:

#include<stdio.h>

struct test
{
char i ;
int j;
char k;
}; int main()
{
struct test temp;
printf("&temp = %p\n",&temp);
printf("&temp.k = %p\n",&temp.k);
printf("&((struct test *)0)->k = %d\n",((int)&((struct test *)0)->k)); }

编译运行,可以得到如下结果:

&temp = 0xbf9815b4
&temp.k = 0xbf9815bc
&((struct test *)0)->k = 8

什么意思看到了吧,自定义的结构体有三个变量:i,j,k。 因为有字节对齐要求,所以该结构体大小为4bytes * 3 =12 bytes.   而&((struct test *)0)->k 的作用就是求 k到结构体temp起始地址的字节数大小(就是我们的size)。在这里0被强制转化为struct test *型, 它的作用就是作为指向该结构体起始地址的指针就是作为指向该结构体起始地址的指针就是作为指向该结构体起始地址的指针, 而&((struct test *)0)->k  的作用便是求k到该起始指针的字节数。。。其实是求相对地址,起始地址为0,则&k的值便是size大小(注:打印时因为需要整型,所以有个int强转)所以我们便可以求我们需要的 size 了  。 好吧,一不小心把 offsetof() 函数的功能给讲完了:::

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这次再看就顺眼了吧(底层为什么是这样我还是不懂。。。只知道这样确实可以) ,  所以offsetof()的作用就是求我们梦寐以求的size, 并以size_t形式返回(size_t: 无符号整型)。

(二) 内核编程的严谨性  

#define container_of(ptr, type, member) ({              \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

这里我们只看第二行:

const typeof( ((type *)0)->member ) *__mptr = (ptr);  

它的作用是什么呢? 其实没什么作用(勿喷勿喷,让我把话说完),但就形式而言 _mptr = ptr,  那为什么要要定义一个一样的变量呢??? 其实这正是内核人员的牛逼之处:如果开发者使用时输入的参数有问题:ptr与member类型不匹配,编译时便会有warnning, 但是如果去掉改行,那个就没有了,而这个警告恰恰是必须的(防止出错有不知道错误在哪里)。。。这严谨性可以吧

typeof( ((type *)0)->member )

它的作用是获取member的类型仅此而已。至此基本结束

(三) 总结

container_of(ptr, type,member)函数的实现包括两部分:

1.  判断ptr 与 member 是否为同意类型

2.  计算size大小,结构体的起始地址 = (type *)((char *)ptr - size)   (注:强转为该结构体指针)

现在我们知道container_of()的作用就是通过一个结构变量中一个成员的地址找到这个结构体变量的首地址。

    container_of(ptr,type,member),这里面有ptr,type,member分别代表指针、类型、成员。

container of()函数简介的更多相关文章

  1. 转"container of()函数简介"链接地址

    https://blog.csdn.net/s2603898260/article/details/79371024 提示关键字: 指针0的使用 typeof的使用

  2. linux进程编程:子进程创建及执行函数简介

    linux进程编程:子进程创建及执行函数简介 子进程创建及执行函数有三个: (1)fork();(2)exec();(3)system();    下面分别做详细介绍.(1)fork()    函数定 ...

  3. Linux Shell系列教程之(十五) Shell函数简介

    本文是Linux Shell系列教程的第(十五)篇,更多Linux Shell教程请看:Linux Shell系列教程 函数可以将一个复杂功能划分成若干模块,从而使程序结构更加清晰,代码重复利用率更高 ...

  4. 【液晶模块系列基础视频】4.5.X-GUI图形界面库-进度条等函数简介

    [液晶模块系列基础视频]4.5.X-GUI图形界面库-进度条等函数简介 ============================== 技术论坛:http://www.eeschool.org 博客地址 ...

  5. 【液晶模块系列基础视频】4.4.X-GUI图形界面库-画tab函数简介

    [液晶模块系列基础视频]4.4.X-GUI图形界面库-画tab函数简介 ============================== 技术论坛:http://www.eeschool.org 博客地址 ...

  6. 【液晶模块系列基础视频】4.3.X-GUI图形界面库-画box函数简介

    [液晶模块系列基础视频]4.3.X-GUI图形界面库-画box函数简介 ============================== 技术论坛:http://www.eeschool.org 博客地址 ...

  7. 【液晶模块系列基础视频】4.2.X-GUI图形界面库-画矩形函数简介

    [液晶模块系列基础视频]4.2.X-GUI图形界面库-画矩形函数简介 ============================== 技术论坛:http://www.eeschool.org 博客地址: ...

  8. 【液晶模块系列基础视频】4.1.X-GUI图形界面库-画线画圆等函数简介

    [液晶模块系列基础视频]4.1.X-GUI图形界面库-画线画圆等函数简介 ============================== 技术论坛:http://www.eeschool.org 博客地 ...

  9. [转]SQLITE3 C语言接口 API 函数简介

    SQLITE3 C语言接口 API 函数简介 说明:本说明文档属作者从接触 SQLite 开始认识的 API 函数的使用方法, 由本人翻译, 不断更新. /* 2012-05-25 */ int sq ...

随机推荐

  1. Hadoop 3.1.1 - Yarn - 使用 FPGA

    在 Yarn 上使用 FPGA 前提 YARN 目前只支持通过 IntelFpgaOpenclPlugin 发布的 FPGA 资源 YARN NodeManager 所在的机器上必须预先安装供应商的驱 ...

  2. ssrf解题记录

    ssrf解题记录 最近工作需要做一些Web的代码审计,而我Web方面还比较薄弱,决定通过一些ctf的题目打打审计基础,练练思维,在博客上准备开几个专题专门记录刷题的过程. pwn题最近做的也很少,也要 ...

  3. postman怎么调中文

    事先准备 1 安装好postman. 下载网址:https://www.postman.com/downloads/ 2 下载好app.zip中文压缩包,下载地址:https://wws.lanzou ...

  4. OpenResty Lua钩子调用完整流程

    前面一篇文章介绍了Openresty Lua协程调度机制,主要关心的是核心调度函数run_thread()内部发生的事情,而对于外部的事情我们并没有涉及.本篇作为其姊妹篇,准备补上剩余的部分.本篇将通 ...

  5. Servelt&&JSP进阶

    Servlet与JSP进阶 来自mkw的视频课程的总结 1.前言 内容包括 掌握Java Web核心特性,Servlet核心对象以及JSP九大内置对象.主要有以下的内容: 请求结构 && ...

  6. 通过Mysql提权的几种姿势

    本文记录利用mysql数据库,在拿到shell之后进行提权的两种方法. 一.UDF提权 原理:UDF是mysql的一个拓展接口,UDF(Userdefined function)让用户通过该接口可以自 ...

  7. SQL 练习37

    检索至少选修两门课程的学生学号 SELECT Student.SId,Student.Sname,选课数 from Student, (SELECT sid,COUNT(cid) 选课数 from s ...

  8. NGINX Ingress控制器1.0.0升级迁移文档(翻译)

    Ingress 是什么 Ingress 是对k8s集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP. Ingress 可以提供负载均衡.SSL 终结和基于名称的虚拟托管. 最近 ...

  9. 9、二进制安装K8s之增加node

    二进制安装K8s之增加node 1.复制文件,要部署几台就直接复制即可 #二进制文件 scp /data/k8s/bin/{kubelet,kube-proxy} root@192.168.100.1 ...

  10. vue:Missing space before value for key 'components'

    原因是Vue对语法比较严格,而eslint是一个语法检查工具,对语法要求极其苛刻严格,于是就error了 解决办法是关闭eslint的语法规则,找到build/webpack.base.conf.js ...