linux内核中的container_of宏

        //xk> 引子

        首先看一个简单的面试题:定义一个宏FIND(stru, e),求结构体stru中某个成员e相对于stru的偏移量。

        题目的解答很简单:

#define FIND(stru, e) &(((stru *)0)->e)

将常量0强制类型转化为stru *类型的指针。因为结构体的首地址为0,所以其成员的地址即为相对于结构体的偏移量。

        顺便提一点,C++中不能直接用&对一个类的成员函数取地址,实现这个目的的技巧参见《C++编程思想》。C++中可以直接用&对一个类的数据成员取地址,因为数据成员和成员函数存放在不同的内存区。


        //xk> 正文

        Linux中广泛运用容器机制。如果struct A中嵌入包含一个子结构B,则称A是B的一个容器。

        比如作为Linux标准数据结构的循环双链表,是由一个一个的表头连接起来的,而这些表头就嵌入在双链表所管理的各种类型的对象之中。注意这里的表头不是指链表的表头,而是链表的元素,是被管理对象在链表中的引子,很多时候我们称被管理对象为链表元素。这样做有两个好处:1. 被管理的对象可能类型迥异,而表头的结构统一而简单。2. 双链表应用非常广泛,因为表头结构统一,内核可以提供一套标准的循环双链表操作。

        通过循环双链表很容易找到链表上的某个表头,我们关注的是怎样通过嵌入在容器中的表头,找到被管理的那个对象的首地址。

        以循环双链表为例,list_entry宏提供了这个功能

// list.h
#define list_entry(ptr, type, member) container_of(ptr, type, member)
而container_of宏则是内核真正用来实现该目的的宏:根据嵌入的子结构成员返回容器首地址。

        根据成员返回结构首地址,简言之:成员的地址 - 成员相对于结构首地址的便宜量 = 结构首地址。linux内核提供offsetof宏来做这个事情

// kernel.h
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)    // 访问结构指针成员操作符-> 的优先级高于 取地址操作符&
        注意操作符优先级:访问结构指针成员操作符-> 高于 取地址操作符& 高于 强制类型转换操作符()

        到这里都很简单,不过下一步形式有点复杂:

// kernel.h
/**
 * container_of 从结构的成员来获得容器实例
 * @ptr:            指向成员数据的指针
 * @type:          容器结构的类型
 * @member:    成员在容器内的名称
 */
#define container_of(ptr, type, member) \
({ \
    const typeof( ((type *)0)->member) *__mptr = (ptr); \
    (type *)( (char *)__mptr - offsetof(type, member) ); \
})
        其核心依然很简单,两步走:

        (1) 创建一个指针__mptr,其值等于ptr。其类型为member的类型。(《深入linux内核架构》说其类型为type,还举了个例子,应该是错的,指针不能隐式类型转换,所以__mptr与ptr类型相同才能赋值)

        (2) __mptr - 偏移量即得到首地址。强制类型转换(char *)是为了保证指针运算以字节为单位。通常指针运算表示指向数组的下一个元素,因此指针 + 1的结果与指针的类型相关。强制类型转换(type *)是因为这个宏的结果应该是容器结构的类型type,返回的是容器结构首地址嘛。

        为啥不直接用ptr,而要生成一个const的__mptr呢?难道container_of宏可能修改ptr?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值