1. #define list_entry(ptr, type, member) container_of(ptr, type, member)

在进行编程的时候,我们经常在知道结构体地址的情况下,寻找其中某个成员的地址;但是知道了成员的地址,如果找到这个结构体对应的地址呢?
Linux内核中,获取节点地址的函数是list_entry(),它的宏定义如上所示。
我们再来查找container_of(ptr, type, member)的定义,发现它依然是一个宏定义:
  1. #define container_of(ptr, type, member) \
  2. ({ \
  3. consttypeof(((type *)0)->member)* __mptr =(ptr); \
  4. (type *)((char*)__mptr - offsetof(type, member)); \
  5. })
在container_of(ptr, type, member)的宏定义中,真正返回节点地址的是最后一句话,
而在最后一句话中offsetof(TYPE, MEMBER)依然是一个宏定义。
  1. #define offsetof(TYPE, MEMBER)((size_t)&((TYPE *)0)->MEMBER)
  1. typedef__kernel_size_tsize_t;
  2. typedefunsignedint__kernel_size_t;
通过逐层查找之后我们来说一下list_entry()函数的具体实现,我们从下往上说起。
 
 
    1. #define offsetof(TYPE, MEMBER)((size_t)&((TYPE *)0)->MEMBER)
  • TYPE
这是我们自定义的结构体类型,它的内部至少一个list_head型成员变量,如下:
  1. struct TYPR
  2. {
  3. //...
  4. struct list_head member;
  5. //...
  6. };
其中list_head也是一个结构体,它的定义我们稍后再说。
  • MEMBER
这是TYPE对象中list_head型变量的变量名。
  • 语句解析
(TYPE *)0:将0强制转换成TYPE型指针,则该指针一定指向0地址(数据段基址)。
&((TYPE *)0)->MEMBER这句话其实是&(((TYPE *)0)->MEMBER),通过该指针访问TYPE的MEMBER成员并得到其地址。
由于该指针的起始地址是0,那么&((TYPE *)0)->MEMBER也就是一个TYPE型变量的起始地址
与该变量内部MEMBER成员变量起始地址之间的偏移量,这个偏移量对于所有的TYPE型变量都是成立的。
那么,接下来的思路便很明确了,我们只要知道一个TYPE类型变量中MEMBER变量的起始地址,减去
offsetof(TYPE, MEMBER)这个偏移量,就可以得到TYPE类型变量的起始地址。
它的的对应关系如下图所示:

 
思路很清晰,但还有一些细节需要注意,我们继续看代码。
 
2.
  1. #define container_of(ptr, type, member) \
  2. ({ \
  3. consttypeof(((type *)0)->member)* __mptr =(ptr); \
  4. (type *)((char*)__mptr - offsetof(type, member)); \
  5. })
  • const typeof(((type *)0)->member) * __mptr = (ptr);
由于下面我们要对指针进行强制类型转换,所以这里我们又申请一个指针,指向和ptr相同的位置。
这里的ptr指的是实际list_head member的地址。
  • (char *)__mptr
由于offsetof()函数求得的是偏移字节数,所以这里(char *)__mptr使得指针的加减操作步长为1Byte
然后二者相减便可以得到TYPE变量的起始地址,最后通过(type *)类型转换,将该地址转换为TYPE类型的指针。
再向上就是一些宏定义没有什么可说的了。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

由结构体成员地址计算结构体地址——list_entry()原理详解的更多相关文章

  1. 节点地址的函数list_entry()原理详解

    本节中,我们继续讲解,在linux2.4内核下,如果通过一些列函数从路径名找到目标节点. 3.3.1)接下来查看chached_lookup()的代码(namei.c) [path_walk()> ...

  2. IP地址、子网掩码详解

    如何通过子网掩码划分网段 资料一: 一.缺省A.B.C类地址,子网掩码:  二.子网掩码的作用:  code:  IP地址 192.20.15.5 11000000 00010100 00001111 ...

  3. CentOS 最新版的下载地址 + 版本选择详解

    CentOS 最新版的下载地址 + 版本选择详解 发现越来越多的机关单位.事业单位开始使用 Linux 作为主要服务器,毕竟,Linux的稳定性和高效性是众所周知的,所以我也打算把自己这一块技术加强一 ...

  4. 【转】C语言中不同的结构体类型的指针间的强制转换详解

    C语言中不同类型的结构体的指针间可以强制转换,很自由,也很危险.只要理解了其内部机制,你会发现C是非常灵活的. 一. 结构体声明如何内存的分布, 结构体指针声明结构体的首地址, 结构体成员声明该成员在 ...

  5. 硬盘内部硬件结构和工作原理详解[zz]

    一般硬盘正面贴有产品标签,主要包括厂家信息和产品信息,如商标.型号.序列号.生产日期.容量.参数和主从设置方法等.这些信息是正确使用硬盘的基本依据,下面将逐步介绍它们的含义. 硬盘主要由盘体.控制电路 ...

  6. 交换机工作原理、MAC地址表、路由器工作原理详解

    一:MAC地址表详解 说到MAC地址表,就不得不说一下交换机的工作原理了,因为交换机是根据MAC地址表转发数据帧的.在交换机中有一张记录着局域网主机MAC地址与交换机接口的对应关系的表,交换机就是根据 ...

  7. java Spring系列之 配置文件的操作 +Bean的生命周期+不同数据类型的注入简析+注入的原理详解+配置文件中不同标签体的使用方式

    Spring系列之 配置文件的操作 写在文章前面: 本文带大家掌握Spring配置文件的基础操作以及带领大家理清依赖注入的概念,本文涉及内容广泛,如果各位读者耐心看完,应该会对自身有一个提升 Spri ...

  8. 小甲鱼PE详解之IMAGE_OPTIONAL_HEADER32 结构定义即各个属性的作用(PE详解03)

    咱接着往下讲解IMAGE_OPTIONAL_HEADER32 结构定义即各个属性的作用! (视频教程:http://fishc.com/a/shipin/jiemixilie/) 接着我们来谈谈 IM ...

  9. 小甲鱼PE详解之IMAGE_NT_HEADERS结构定义即各个属性的作用(PE详解02)

    PE Header 是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,里边包含着许多PE装载器用到的重要字段.下边小甲鱼将为大家详细讲解哈~ (视频教程:http://fishc.co ...

随机推荐

  1. PHP获取图片主题颜色

    (1)工具类:pictureColor.php class pictureColor{     /**     * 获取颜色使用库类型     */    public $type = 'gd';   ...

  2. CSS3 grayscale滤镜+SVG使图片变黑白实例页面

    CSS代码: .gray { -webkit-filter: grayscale(100%); -moz-filter: grayscale(100%); -ms-filter: grayscale( ...

  3. 如何修改Windows程序的权限?

    修改程序的权限需要用到3个函数: 1. 获取进程的令牌句柄: OpenProcessToken 2. 查找特权类型的ID: LookupPrivilegeValue 3. 修改进程的特权:Adjust ...

  4. c语言条件编译和预编译

    转自http://www.cnblogs.com/rusty/archive/2011/03/27/1996806.html 一.C语言由源代码生成的各阶段如下: C源程序->编译预处理-> ...

  5. bzoj 2002

    2002: [Hnoi2010]Bounce 弹飞绵羊 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 12203  Solved: 6162[Subm ...

  6. 我们使用git checkout 将B分支上的部分页面代码 添加或覆盖到A分支上

    $ git branch * A B $ git checkout B message.html message.css message.js other.js $ git status # On b ...

  7. Docker Swarm高可用性

    一.前言 在Docker Swarm集群中,Swarm manager负责管理整个集群,如果管理节点manager出现故障,虽然不会影响现有的服务和工作节点,但是我们不能继续管理我们的docker s ...

  8. Git1:Git简介

    目录 什么是版本控制系统 集中式版本控制系统 分布式版本控制系统 Git起源 Git特性 什么是版本控制系统 版本控制系统是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统.版本控制 ...

  9. 掌握Markdown

    翻译自 https://guides.github.com/features/mastering-markdown/ 转载请注明链接 掌握Markdown Markdown是Github平台上一种轻量 ...

  10. Java并发编程原理与实战三十二:ForkJoin框架详解

    1.Fork/Join框架有什么用呢? ------->Fork使用来切分任务,Join是用来汇总结果.举个简单的栗子:任务是1+2+3+...+100这个任务(当然这个任务的结果有好的算法去做 ...