源代码笔记——container_of

近日在看红黑树的相关知识,在Linux的红黑树源代码中看到了container_of,甚是不解,于是仔细看了一下,总结一下。

首先在include\linux\kernel.h中找到定义如下:

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

扫描一遍这个宏定义,发现有如下几个疑问:

(1)typeof是什么?未在c语言中(c++)见过;

(2)((type*)0)的用法;

(3)offsetof又是什么意思?

(4)这个宏定义的用法,以及实现的功能。

下面一个个来看:

(1)typeof

搜索一下发现是c语言新加的关键字,在linux系统中广泛使用。

使用方法:typeof(类型) 、 typeof(表达式)

测试一下:

int a = 10;
typeof(&a) pa = &a;
printf( "%d\n", *pa );
或者

int a = 100;
typeof(int *) pa = &a;
printf( "%d\n", *pa );

因此typeof的作用是取出括号中的类型,并且可以用来定义新的变量,于是可以将第一句简化为

const typeof( 表达式) * mptr=(ptr);    //就是定义了一个新的变量,变量的类型是表达式中的指针类型。

(2)((type*)0)->member

根据字面意思type应该是一种类型,又其包含数据成员member,所以是一个struct(在C语言中),

仔细想一下这个0的用法,是将0强制转换为一个指针,然后访问其member成员,由于现在的水平有限,

可以先这样理解:存在地址addr,假设其指向对象obj(类型为struct exper),将其转化为struct exper*,

并取出其member成员。细想一下若是发生在运行期间会出现访问0地址内存的错误,结合上述typeof

可以得出typeof( ((type*)0)->member )应该是一种编译期间动作,得出type结构中member 成员

的类型。

(3)offsetof

在include\linux\stddef.h中找到定义如下:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
同样((TYPE*)0)得到一个TYPE类型的指针,取出其MEMBER成员,细想一下可以这么理解,

0可以认为是这个结构体对象的基地址,从这个基地址开始取出某个成员MEMBER,再取出这个MEMBER

成员的地址:

成员MEMBER的地址 = 基地址(此处为0)+ 成员MEMBER在结构体中的偏移量

此处&(((TYPE*)0)->MEMBER)求的就是TYPE结构体重成员MEMBER的地址,因此得出的值就是

MEMBER在TYPE结构中的偏移量。

(4)宏定义的功能和用法

const typeof( ((type *)0)->member ) *__mptr = (ptr);  //定义一个type中成员变量member类型的指针

同时这个指针用宏参数ptr初始化,可见ptr本身就是一个 type成员变量member类型的指针;

(type *)( (char *)__mptr - offsetof(type,member) );   //经过偏移计算,得出的是一个(type*)指针

根据上面的偏移公式,这里计算的是:  成员member的地址  - 成员member在结构体中的偏移量

因此得出的就是该结构体的基地址,也就是起始地址。

从而,可以将该宏的功能总结一下:根据结构体中某个成员的地址,得出这个结构体本身的地址。

下面举个例子说明一下这个宏的用法:

假设有结构体如下:

struct dma_chan_ref {
	struct dma_chan *chan;
	struct list_head node;
	struct rcu_head rcu;
	atomic_t count;
};
函数如下:

static void free_dma_chan_ref(struct rcu_head *rcu)
{
	struct dma_chan_ref *ref;
	ref = container_of(rcu, struct dma_chan_ref, rcu);
	kfree(ref);
}
以上代码摘抄自linux源代码,但是还不知道是什么意思,此处只是引用一下,说明一下本文的问题:

函数需要根据结构体(struct dma_chan_ref)中成员rcu(struct rcu_head)的地址来获得结构体

struct dma_chan_ref的起始地址,然后进行kfree,调用参数如下:

1)第一个参数rcu为函数中的形参,代表的是结构体中rcu成员的地址;

2)第二个参数是基础结构体的名字struct dma_chan_ref;

3)第三个参数rcu代表的是在基础结构体struct dma_chan_ref中第一个指针参数rcu实体在结构体中

     的名字。

也就是第一个参数是个结构体成员的指针,第二三两个参数分别是结构体名字和成员名字。



你可能感兴趣的:(源代码笔记——container_of)