金盛豪

金盛豪的分享

他的个人主页  他的分享

container_of分析

金盛豪   2010年08月06日 星期五 20:41 | 0条评论

内核源码中存在这么一个宏container_of
用于从一个结构体的成员指针获取结构体的指针。
如:

      1
      
2
3
4
      
       struct
      
      my
      
       {
      
      
int a ;
int b ;
} ;

在此如果知道成员b的指针可用container_of函数来获取包含b的my结构体的指针。
在此列中使用如以知b的指针pb:

      1
      
2
      
       struct
      
      my
      
       *
      
      getmy
      
       ;
      
      
getmy = container_of ( pb , struct my , b ) ;

这样就获取到了my的指针。
这个宏的工作原理比较简单,下面简单介绍下其工作原理和实际代码。

大体思路是,获取b成员相对my的偏移量,然后从b的指针(地址)减去偏移量就能获取到指向my的指针。
实际的代码在内核include/linux/kernel.h中定义,如下:

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

挺简单就两行语句。
第一句定义一个变量来__mptr来保存ptr指针。
这里使用了一个typeof操作符,此操作符由GCC编译器指定的,并不是标准操作符,用来返回变量的类型。
其中包含 ((type *)0)->member 这里把0强制转换为 type类型的指针(这种把零转换为特定指针的操作下面还会遇到)。然后获取其成员,并当typeof的参数来获取其成员的类型。
这里复制指针可能是为了防止操作时误改写指针的值。是不是这个用意我也无从得知了(*^__^*) 。
接下就是关键的减肥来获取需要的指针。 这里又有个宏offsetof 用来获得type结构体的member成员的偏移量,稍候我们会解释。
这里我们看到整体框架用花括号来包起来。这是为了在内部定义的__mptr 称为局部变量(也就是在这个花括号外边无法使用),以及让整条语句的值结果为第二个语句所执行的结果。
接下来我们看下offsetof宏,这个宏在 include/linux/stddef.h中定义
如下

      1
      
2
3
4
5
      
       #ifdef __compiler_offsetof
      
      
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

这里有两种方法,如果编译器支持直接获取。就调用编译器的方法来获取,这个我们没兴趣。下面来看看内核的实现。
&((TYPE *)0)->MEMBER)这里和上面说过的一样。强制转换0为需要的指针,并且获取其成员,当然这里的成员是非法的,如果进行读写会产生不可预知的错误。
前面有个取地址符来获取该成员的指针,这里我们想一下。如果结构体的地址为0,其成员的地址不就是该结构体中成员的偏移量吗!!!o(∩∩)o…
然后是强制类型转换为size_t型来返回。
如果看过内核当中的链表实现你可能会看过list_entry这个宏,感觉两个宏操作差不多,我们来看看实际代码,在文件include/linux/list.h中。

      1
      
2
      
       #define list_entry(ptr, type, member) \
       
container_of(ptr, type, member)

果不其然。list_entry 其实就是一个照搬container_of的宏。

评论

我的评论:

发表评论

请 登录 后发表评论。还没有在Zeuux哲思注册吗?现在 注册 !

暂时没有评论

Zeuux © 2024

京ICP备05028076号