linux内核之container_of()详解(即:list_entry()的详解)

        在linux内核链表中,有两个点我认为设计的比较巧妙。

        第一个是链表项中没有数据项,而是在要使用链表时,定义个数据项结构体,然后把链表项结构体定义为数据项结构体的成员(这个将在下一篇博文中详细阐述)。如果要对数据项结构体中数据进行访问,则通过本文要分析的函数(或者说宏)来解决。

        第二个是container_of(ptr, type, member)这个宏,这个宏的作用是已知一个结果体的类型type,和一个结构体的成员变量member,(ptr = &member)。就可以求的出结构体的首地址。其实这是解决上一个设计巧妙的地方所带来的问题。

先来看下这个宏的用处:

        假设:有个结构体typedef  struct  test{......}T;其结构体成员都知道的(假设里面有个成员变量为member,他所在的结构体中的位置随意)。现在已知某个(未知结构体变量名)结构体变量中的成员变量member,求该结构体变量中的其他成员变量。

        分析:求其他结构体变量中的成员,其实只要求出member所在的结构体变量的首地址即可,通过首地址指针一个个遍历。

        答案:用container_of()宏来求解。这个宏就是用来求 已知某个结构体变量中的某个成员所在的结构体变量首地址。member所在的结构体变量首地址 = container_of(&member,T,member);

        接下来分析下container_of()宏的实现:

        #define list_entry(ptr, type, member) /
                ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

        先来逐个分析下:

        减号右边分析:(unsigned long)(&((type*)0)->member)     (type*)0: 把0地址强制转换成type结构体的地址,表示0地址处存放了type类型的结构体变量,为什么在0处强转为结构体地址,这也是个设计巧妙点;((type*)0)->member:这个表示指向结构体变量中的成员member;&(((type*)0)->member)):这个当然是结构体变量中member的地址了,但是这里转了下弯,这是不仅表示是结构体变量中成员member的地址,还表示了结构体变量中的member成员到结构体变量的首地址的距离,也就是说member相对于首地址的偏移量。偏移量本应该是这么算的:member的地址 - 首地址。但首地址强转在0处,所以member的地址就是偏移量。(unsigned long)是把偏移量强转为整型。

        减号左边的就很好分析了。用实际的member地址减去偏移量,就得到了变量的首地址了。然后再强转为结构体类型。(char*)是因为前面的偏移量是用unsigned long来表示的。大概原理就是这样的了。其实很简单,关键点是在0处强转为结构体类型,以至于得到member的地址就是偏移量。

        请看下面原理图:


        有关于container_of()的实例小程序请看:http://blog.csdn.net/yuzhihui_no1/article/details/38407443




展开阅读全文

没有更多推荐了,返回首页