container_of源码分析


在linux/android代码中经常能看到container_of这个函数。
container_of(ptr,type, member),

  1. type是一个结构体,调用container_of时type一般会赋值为struct xxx
  2. member是结构体中的成员名称,而ptr是指向这个成员的指针,即存储该成员在存储空间里的首地址。
  3. 调用后,container_of会返回这个结构体在存储空间里的首地址。

这次抽时间去学了下container_of具体的实现机制。本文代码基于android10,rk3399平台代码。

container_of宏定义

定义位于:rk3399-android-10/kernel/include/linux/kernel.h

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:        the pointer to the member.
 * @type:       the type of the container struct this is embedded in.
 * @member:     the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({                              \
        void *__mptr = (void *)(ptr);                                   \
        BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&   \
                         !__same_type(*(ptr), void),                    \
                         "pointer type mismatch in container_of()");    \
        ((type *)(__mptr - offsetof(type, member))); })

分析BUILD_BUG_ON_MSG

先看这个函数的第一个参数:
!__same_type(*(ptr), ((type *)0)->member) && !__same_type(*(ptr), void)
__same_type定义位于:rk3399-android-10/kernel/include/linux/compiler_types.h

/* Are two types/vars the same type (ignoring qualifiers)? */
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

__builtin_types_compatible_p函数能判断a和b的类型是否相同,相同返回1,否则返回0。
所以这句&&语句里,只要有一个same_type返回1,这条语句就会为0;只有两个same_type均返回0,这句话才为1。

再看BUILD_BUG_ON_MSG的宏定义,位于:rk3399-android-10/kernel/include/linux/build_bug.h

/**
 * BUILD_BUG_ON_MSG - break compile if a condition is true & emit supplied
 *                    error message.
 * @condition: the condition which the compiler should know is false.
 *
 * See BUILD_BUG_ON for description.
 */
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)

而其中compiletime_assert的宏定义,位于:rk3399-android-10/kernel/include/linux/compiler.h

/**
 * compiletime_assert - break build and emit msg if condition is false
 * @condition: a compile-time constant condition to check
 * @msg:       a message to emit if condition is false
 *
 * In tradition of POSIX assert, this macro will break the build if the
 * supplied condition is *false*, emitting the supplied error message if the
 * compiler has support to do so.
 */
#define compiletime_assert(condition, msg) \
        _compiletime_assert(condition, msg, __compiletime_assert_, __LINE__)

由上注释知,当compiletime_assert的参数condition为false时,会发送msg出去。

故,对于BUILD_BUG_ON_MSG来说,当!(cond) == false,也就是cond == true时,它的msg会被发送并停止编译。
回到container_of的宏定义,知当!__same_type(*(ptr), ((type *)0)->member) && !__same_type(*(ptr), void) == true时,也就是两个same_type均不匹配时,会报错"pointer type mismatch in container_of()"。

再回来细看&&语句。
前面半句:!__same_type(*(ptr), ((type *)0)->member) 。(type *)0)的意思是,假设从地址0开始,强制转换成type结构体来读取,即读取的是地址0-(sizeof(type) -1)存储这个type结构体。所以这半句是判断指针ptr存储的值是否和结构体type中的成员member同一个类型。
后面半句: !__same_type(*(ptr), void) ,判断指针ptr存储的值是否为void类型。

分析offsetof

((type *)(__mptr - offsetof(type, member)));
container_of宏函数定义的第一句写了,__mptr为void*类型的指针,指向ptr指向的地址。
offsetof定义位于:rk3399-android-10/kernel/include/linux/stddef.h

#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE, MEMBER)  __compiler_offsetof(TYPE, MEMBER)
#else
#define offsetof(TYPE, MEMBER)  ((size_t)&((TYPE *)0)->MEMBER)
#endif

如果已经定义了__compiler_offsetof宏函数,就调用__compiler_offsetof函数。

__compiler_offsetof定义位于:rk3399-android-10/kernel/include/linux/compiler_types.h

#define __compiler_offsetof(a, b)       __builtin_offsetof(a, b)

再来分析offsetof定义的else分支:

假设有
struct Test {
int a;
...
int member;
...
}
struct Test *A;

则正常存储在内存中的图示应如下图图1:调用container_of(ptr, type, member),ptr指向结构体中member成员的存储首地址,container_of应该返回A指向的结构体在内存中的首地址。
图1
而假设从地址0处开始存储一个struct Test,令指针A指向这个结构体,则(A->member的地址-0)就是从结构体首部到member首部的地址差值。图示见图2:
图2
再把差值(&((TYPE *)0)->MEMBER)转换成size_t类型,作为offsetof的返回值。
((type *)(__mptr - offsetof(type, member)));这句话就表示,用图1中的ptr指向的地址减去通过图2算出的与结构体首部的地址差,就能得到结构体首部地址。
结构体首部地址 = ptr指向地址 - 地址差

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值