container_of,是linux内核中一个精妙绝伦的宏。上年在南京开发新产品应用程序时,一个同事借用linux的实现,在应用程序中实现了该宏。之前,曾经弄懂过它,许久未接触linux了,对它有点陌生了。只清楚记得它的作用就是:根据结构体成员得到该结构体指针。
在网上看了多篇关于container_of的解释,觉得还是华清远见程姚根讲得简单透彻。
以下内容整理自:http://www.embedu.org/Column/Column433.htm
linux 内核中有一个大名鼎鼎的宏:container_of(),这个宏是用来干嘛的呢?先看看它在内核中的定义。
乍一看不知道是什么东东。没关系,我们一点一点来揭开它的面纱。
先来分析一下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);
其中,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地址的偏移量。也许现在你会问,整出这么个玩意干嘛,下面看个列子:
程序运行结果:
发现没有,如果把上图中第二个值减去最后一个值,就得到第一个值。也就是:
(&temp.k) – (&((struct test *)0)->k) = &temp
再回首一下它,就彻底明白了container_of()宏的实现原理了:
(struct test *)((char *)__mptr - offsetof(struct test,j));
是不是可以获得结构体变量temp的首地址呀,是不是太精妙了呀,linux内核中随随便便一个宏就有如此精妙,想想对linux了解非常多的牛人,还有很长一段路。