在内核的原码文件 kernel.h文件中,定义了一个宏container_of,如下:
/** * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) //这个宏不是在kernel.h文件中定义的。
ptr:指向成员的指针。
type:需要返回的结构实例类型。
member:成员在结构实例内部的名称,如果为数组,需要指定下标。
下面分析一下这个宏,举个例子:
假设下面的这个结构:
struct pid { atomic_t count; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; struct rcu_head rcu; int level; struct upid numbers[4]; };最后一个本来不是4,而是1,我为了便于理解,将其改成4。
我们的需求是:根据结构体内的struct upid number[2]来获取这个结构实例的指针,假设已经知道struct upid number[2]的指针是pnr,如下定义:
按照这个宏的定义目标,我们可以这么实现:
struct upid *pnr; struct pid *_pid = container_of(pnr, struct pid,numbers[ns->level]); //根据struct upid numbers[ns->level]来返回struct pid的实例指针。
offsetof宏:先将空指针0转换为一个指向struct pid的指针,然后获取成员的地址,因为是从0开始的,所以这个地址就是成员相对于struct pid结构的偏移量。
因为内存的使用是往上增长的,所以成员自己的地址减去成员在结构中的偏移量就是结构实例的地址了。
这个宏是内核的一个使用技巧,很多地方都是这样根据成员的地址然后找到结构实例的地址。