linux内核之container_of()应用

来源:互联网 发布:家庭健身器材 知乎 编辑:程序博客网 时间:2024/06/08 23:27

        本来不打算写这篇博文的,因为我觉得在 linux内核之container_of()详解  文章中已经讲的非常详细了,再写什么也觉得是狗尾续貂了,没必要。可是今天自己做openvswitch内核开发时,发现自己也用错几个地方。然后又回想下记得有个博友说过一句话:聪明的作者不仅能自己看懂,还能深入浅出的举些简单的例子帮助别人理解。所以就写个简单的程序来说明下怎么使用container_of()这个宏吧。

        如果想了解container_of()的工作原理,我建议还是先看下linux内核之container_of() 详解这篇文章。如果只想用它,或者说使用时出现些莫名的错误。那么请看下面的小程序。

   这个程序只是用来说明怎么用container_of()这个宏,所以在程序中有些细节(动态申请时没判断是否申请成功,动态分配的内存没有销毁等)没去过多的处理,希望大家谅解。

        先分析程序,最后再把运行结果的图片贴上。说下几处关键点:

先来看第30行的代码,ptr_a = test_init();这是获取个int型的指针,联系test_init()函数可知也是把结构体变量中int a的地址传过来。所以记住关键点一:ptr_a是结构体变量中的成员a的地址。

        再来看第32行的代码,t1 = container_of(ptr_a,T,a);这是行最关键的代码了。这个返回的是指向成员a(ptr_a是a的地址)所在的结构体变量的指针。说下里面的参数,

       第一个参数:ptr_a   这是个关键性参数,因为有ptr_a所以才能和成员a所在的结构体变量联系起来。记住这是个结构体成员的地址,而不能认为是个指针,因为如果结构体成员是个指针,那么这个参数就是指针的地址(二级指针)。再如果这个成员是个结构体变量,那么这个参数也是结构体变量的地址。记住一定是结构体成员的地址,如果想知道为什么会这样,还是请你详细的分析下这个宏就知道了(当然了也可以看linux内核之container_of() 详解);

       第二个参数:T,这是结构体变量的类型,这个是一定要这么填的。

       第三个参数:a,很多人会栽在这个参数手上。很多人会这么写:T->a或者T.a等等。其实这只要填个与第一个参数对应的成员变量名就可以,绝对不需要用结构体变量来指向(T->  )。所谓与第一个变量对应就是说:如果第一个参数是 &(t->c)  那么相对于的第三个参数就直接写  c就可以了。

        这个大概的就分析这么些,下面贴上运行结果:

        本来有段工作上出现错误的代码,但因为是这个涉及到公司的工作所以删除了。见谅!如果涉及到内核中 hlist_for_each_entry(),hlist_for_each_entry_continue(),hlist_for_each_entry_safe()等等,这类宏的话,就要小心宏中的 hlist_entry(ptr, type, member)  出问题了,#define hlist_entry(ptr, type, member) container_of(ptr,type,member)  所以总结来说还是要小心使用 container_of()宏。如果真遇到使用hlist_for_each_entry()这类宏出错的话,可以查看下宏中调用的container_of()是否出错了,参数是否对了。

        今天工作上发现的问题,想了下还是写下来供大家借鉴下,以免犯同样的错。就是如果大家程序中要用到这个宏时,注意结构体中尽量不要使用指针。我设计的结构体是struct dataNode{ struct hData data; struct hlist_node *node};这个是做哈希链表的,设计的数据哈希链表结构见下图:

                  

        了解内核中链表或者哈希链表结构的都知道,里面是没有数据项的,要我们自己使用时,自行定义数据结构体(如文中的struct  dataNode结构体),然后包含内核中定义链表结构体(struct hlist_node *node)。而我们定义的数据结构体(dataNode)是靠内核中定义的链表结构体节点(struct hlist_node *node)来连接的(我们对这种链表结构的操作一般都是使用内核设计好的一些操作宏或者函数,这些宏或者函数都是对没有数据项的链表节点进行操作的)。所以一般是已知内核链表节点(struct hlist_node *node)通过container_of() 来求出数据节点(struct  dataNode)的头指针。

        问题来了,如果已知数据节点中的成员node是可以推导出dataNode指针的。但如果仅仅是知道node所指向的数据,则是不能由container_of()推导出。  (为什么不能推导出来,稍微看下就知道,因为这样就相当于是个单链表了,单链表是无法推出前个节点的地址)而我们程序中操作时,往往是传递过来的是某个链表节点也就是node指向的数据。所以这种链表的结构是个误区,要多小心。




1 0