

有些宏经常出现还是得搞清楚底层是怎么写的,面试还可能会问到的。
Linux 内核中的container_of是一个通过结构体成员的指针反向获取外层结构体指针的宏。它是内核开发中常用的核心技巧,尤其在链表、设备驱动等场景中广泛使用。
1.container_of宏的核心思想
container_of宏的核心思想是通过计算结构体成员在结构体内的偏移量,从成员指针反推出整个结构体的地址。具体步骤如下:
计算偏移量:确定成员在结构体中的偏移量(以字节为单位)。
地址转换:将成员指针转换为字节指针(char*),以便进行精确的字节级指针运算。
逆向计算:从成员地址减去偏移量,得到结构体的起始地址。
container_of 的典型定义如下(位于 <linux/kernel.h>):

参数:
ptr:结构体成员的指针(如 &dev->list)。
type:外层结构体的类型(如 struct device)。
member:成员在结构体中的名称(如 list)。
返回值:外层结构体的指针(如 struct device *)。
2. 实现原理
核心步骤
类型检查:
const typeof( ((type *)0)->member ) *__mptr = (ptr);
typeof获取成员的类型,确保ptr与member类型匹配,否则编译器报错。
创建一个临时指针__mptr,避免直接修改原始指针ptr。
static_assert 和 __same_type 检查 ptr 的类型是否与 type->member 的类型一致,避免误用。
计算成员偏移量:
使用 offsetof(type, member) 计算成员 member 在结构体 type 中的偏移量。
// offsetof 的实现通常依赖于编译器扩展 #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
将地址 0 强制转换为 TYPE* 类型,此时 &((TYPE *)0)->MEMBER 即为成员 MEMBER 的偏移量。
反向计算外层结构体地址: 通过成员指针 ptr 减去其偏移量,得到外层结构体的起始地址。
(type *)( (char *)ptr - offsetof(type, member) )
将 ptr 转换为 char* 类型以确保指针按字节计算偏移。
计算结果为结构体的起始地址,最终转换为type *类型。
举一个例子
假设存在以下结构体:
struct person {int age;char name[32];struct list_head node; // 链表节点};//通过node指针获取person结构体:struct list_head *node_ptr = &some_person.node;struct person *p = container_of(node_ptr, struct person, node);
3. 典型应用场景
链表遍历(内核链表实现)
Linux 内核的链表实现(struct list_head)大量使用 container_of。例如:
struct device {int id;struct list_head list; // 链表成员};// 遍历链表时,通过 list_head 找到外层 device 结构体struct list_head *pos;list_for_each(pos, &device_list) {struct device *dev = container_of(pos, struct device, list);// 操作 dev->id 等}
list_headstruct device 中,通过 container_of 可以从链表节点反推外层结构体。在设备驱动中,通过回调函数的参数(如 struct device * 的某个成员)获取外层设备结构体。
({ ... })为GCC的语句表达式,允许宏返回一个值。typeof是GCC扩展,非标准C特性。
ptr确实指向type结构体的member成员,否则导致未定义行为。typeof用于获取变量类型。({ ... })是 GCC 的语句表达式(Statement Expression),允许在宏中执行多步操作并返回结果。
container_of宏通过巧妙的指针运算和编译时类型检查,实现了从成员到父结构体的高效逆向查找。其核心优势在于:
container_of 是 Linux 内核中 “从成员反推父对象” 的核心工具,其本质是通过指针运算和类型转换实现地址计算。它体现了以下思想:
理解 container_of 是掌握 Linux 内核数据结构设计的关键一步。
