详解Linux内核中的container_of函数
前言
在linux 内核中,container_of
函数使用非常广,例如 linux内核链表 list_head
、工作队列work_struct
中。
在linux内核中大名鼎鼎的宏container_of()
,其实它的语法很简单,只是一些指针的灵活应用,它分两步:
第一步,首先定义一个临时的数据类型(通过typeof( ((type *)0)->member
)获得)与ptr
相同的指针变量__mptr
,然后用它来保存ptr
的值。
第二步,用(char *)__mptr
减去member
在结构体中的偏移量,得到的值就是整个结构体变量的首地址(整个宏的返回值就是这个首地址)。
那这个宏到底是用来干嘛的呢?我们先来看看它在内核中是怎样定义的。
呵呵,乍一看不知道是什么东东。
我们先来分析一下container_of(ptr,type,member)
,这里面有ptr
,type
,member
分别代表指针、类型、成员。
看一个例子:
Struct test { int i; int j; char k; }; Struct test temp;
现在呢如果我想通过temp.j的地址找到temp的首地址就可以使用container_of(&temp.j,struct test,j);
现在我们知道container_of()
的作用就是通过一个结构变量中一个成员的地址找到这个结构体变量的首地址。
下面来看看比较复杂的内容:
我们用上面的struct test张展一下
Const typeof(((struct test *)0)->j) * __mptr = (&temp.j); //(sturct test *)0 表示数据段基址
其中,typeof
是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型。因此,上述代码的作用是首先使用typeof
获取结构体成员j的类型为int
,然后顶一个int指针类型的临时变量__mptr
,并将结构体变量中的成员的地址赋给临时变量__mptr
。
(struct test *)((char *)__mptr - offsetof(struct test,j));
接着我们来看一下offsetof(struct test,j)
,他在内核中如下定义
展开(size_t)&((struct test *)0)->j
,这是什么东东?
一开始也不明白,这里要感谢曹忠明老师的热心帮助,一语惊醒梦中人,呵呵,可以是这样理解。
其中size_t
是整型,那么我们可以知道最终的结果是一个整形值,也就是j相对于0地址的偏移量。也许现在你会问,整出这么个玩意干嘛,下面看个列子:
程序运行结果:
发现没有如果把第二个值 减去最后一个值,就能得到第一个值。
在回首一下它:
(struct test *)((char *)__mptr - offsetof(struct test,j));
是不是可以获得结构体变量temp的首地址呀,是不是太精妙了呀
总结
linux内核中随随便便一个宏就有如此精妙,想想对linux了解非常多的牛人,还有很长一段路。好了,以上就是本文的全部内容了,希望这篇文章的内容对大家的工作和学习能有所帮助,如果有疑问可以留言交流,谢谢大家对我们的支持。