转载:http://www.cnblogs.com/skywang12345/p/3562146.html

倘若你查看过Linux Kernel的源码,那么你对 offsetof 和 container_of 这两个宏应该不陌生。这两个宏最初是极客写出的,后来在Linux内核中被推广使用。

1. offsetof

1.1 offsetof介绍

定义:offsetof在linux内核的include/linux/stddef.h中定义。

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

说明:获得结构体(TYPE)的变量成员(MEMBER)在此结构体中的偏移量。
(01)  ( (TYPE *)0 )   将零转型为TYPE类型指针,即TYPE类型的指针的地址是0。
(02)  ((TYPE *)0)->MEMBER     访问结构中的数据成员。
(03)  &( ( (TYPE *)0 )->MEMBER )     取出数据成员的地址。由于TYPE的地址是0,这里获取到的地址就是相对MEMBER在TYPE中的偏移。
(04)  (size_t)(&(((TYPE*)0)->MEMBER))     结果转换类型。对于32位系统而言,size_t是unsigned int类型;对于64位系统而言,size_t是unsigned long类型。

1.2 offsetof示例
代码(offset_test.c)

 

结果

gender_offset = 0
id_offset = 4
age_offset = 8
name_offset = 12

说明:简单说说"为什么id的偏移值是4,而不是1"。我的运行环境是linux系统,32位的x86架构。这就意味着cpu的数据总线宽度为32,每次能够读取4字节数据。gcc对代码进行处理的时候,是按照4字节对齐的。所以,即使gender是char(一个字节)类型,但是它仍然是4字节对齐的!

1.3 offsetof图解

TYPE是结构体,它代表"整体";而MEMBER是成员,它是整体中的某一部分。
将offsetof看作一个数学问题来看待,问题就相当简单了:已知'整体'和该整体中'某一个部分',而计算该部分在整体中的偏移。

2. container_of

2.1 container_of介绍

定义:container_of在linux内核的include/linux/kernel.h中定义。

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

说明:根据"结构体(type)变量"中的"域成员变量(member)的指针(ptr)"来获取指向整个结构体变量的指针。
(01) typeof( ( (type *)0)->member )     取出member成员的变量类型。
(02) const typeof( ((type *)0)->member ) *__mptr = (ptr)    定义变量__mptr指针,并将ptr赋值给__mptr。经过这一步,__mptr为member数据类型的常量指针,其指向ptr所指向的地址。
(04) (char *)__mptr    将__mptr转换为字节型指针。
(05) offsetof(type,member))    就是获取"member成员"在"结构体type"中的位置偏移。
(06) (char *)__mptr - offsetof(type,member))    就是用来获取"结构体type"的指针的起始地址(为char *型指针)。
(07) (type *)( (char *)__mptr - offsetof(type,member) )    就是将"char *类型的结构体type的指针"转换为"type *类型的结构体type的指针"。

2.2 container_of示例

代码(container_test.c)

 

结果

gender= 1
age= 24
name= zhouxingxing

2.3 container_of图解

type是结构体,它代表"整体";而member是成员,它是整体中的某一部分,而且member的地址是已知的。
将offsetof看作一个数学问题来看待,问题就相当简单了:已知'整体'和该整体中'某一个部分',要根据该部分的地址,计算出整体的地址。

linux内核源码中两个重要的宏的更多相关文章

  1. linux 内核源代码情景分析——linux 内核源码中的汇编语言代码

    1. 用汇编语言编写部分核心代码的原因: ① 操作系统内核中的底层程序直接与硬件打交道,需要用到一些专用的指令,而这些指令在C语言中并无对应的语言成分: ② CPU中的一些特殊指令也没有对应的C语言成 ...

  2. Linux 内核源码中likely()和unlikely()

    ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢? 首先明确: if (likely(value))等价于if (value)if (likely( ...

  3. Linux内核源码中的likely和unlikely释疑【转】

    本文转载自:https://my.oschina.net/armsky/blog/15320 ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢? 首 ...

  4. Linux 内核源码中likely()和unlikely()【转】

    本文转载自:http://blog.csdn.net/tigerjibo/article/details/8279183 ikely()与unlikely()在2.6内核中,随处可见,那为什么要用它们 ...

  5. 如何单独编译Linux内核源码中的驱动为可加载模块?

    答: 分为两步: 1. 配置某个驱动为模块(如: CONFIG_RTC_XXX=m) 2. 指定路径并编译, 如编译drivers/rtc中的驱动 make SUBDIRS=drivers/rtc m ...

  6. linux内核源码中常见宏定义

    http://blog.csdn.net/yangdelong/article/details/5508057

  7. 盘点Linux内核源码中使用宏定义的若干技巧(1)

    http://blog.chinaunix.net/uid-23769728-id-3141515.html

  8. Linux内核源码特殊用法

    崇拜并且转载的: http://ilinuxkernel.com/files/5/Linux_Kernel_Source_Code.htm Linux内核源码特殊用法 1 前言 Linux内核源码主要 ...

  9. <Linux内核源码>文件系统VFS内核4.0.4版本基本概念源码

    题外话:Linux内核从2.x和3.x到现在最新的4.x变化非常大,最直观的表现就是很多书上的内核代码已经无法直接继续使用,所以看看新的源码是非常有意义的! (下文中的内核源码都来自于 kernel ...

随机推荐

  1. MySQL☞dual虚拟表

    Dual表:虚拟表,专门用来测试各种函数:(本来以为跟Oracle中的dual表一样,发现还是不太一样)

  2. 第十篇 Python的字符串格式化

    字符串格式化:就是按照你的意愿做一个拼接的过程. 1. 字符串格式化的第一种方式:百分号方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存. %[ ...

  3. [Effective Python] 用Pythonic方式来思考

    Effective Python chap.1 用Pythonic方式来思考 Pythonic: 一门语言的编程习惯是由用户来确立的. 1. 确认自己所使用的Python版本 2. 遵循PEP8风格指 ...

  4. [leetcode-655-Print Binary Tree]

    Print a binary tree in an m*n 2D string array following these rules: The row number m should be equa ...

  5. php+Mysql 页面登录代码

    登录界面设置: <?php/** * Created by xx. * User: msi * Date: 2017/10/26 * Time: 18:12 *///session每次用之前都要 ...

  6. edgeboxes proposal 和dpm 连接

    经过多天阅读文献和代码,我对edgeboxes 和 dpm的有了一定了解. 现在决定把它们简单地连接起来 也就是edgeboxes proposal 推荐窗口 然后 dpm去判断 但按照我理解的DPM ...

  7. 自定义Json格式

    老铁们都知道,一般的json格式就是键值对格式,在一些特定的框架或者系统中,会用到自定义格式的json文件,假设我们要得到的特定格式json格式如下: {"A":"2&q ...

  8. xml解析标签

    //获取两个标签之间的值 private static string GetStr(string message, string strStart, string strEnd) { ; ; star ...

  9. neutron DVR

    DVR 简介 DVR 提出的背景 在 Neutron 的网络环境中,跨子网的虚机通信是需要通过 Neutron 的路由器.这既包括不同子网的虚拟机之间的通信,又包括虚拟机与外网之间的通信.在 DVR ...

  10. hdu 1249 三角形 (递推)

    三角形 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...