当前位置:首页>Linux>Linux container_of宏

Linux container_of宏

  • 2026-08-27 11:29:14
Linux container_of宏
大家好,我是王鸽,今天这篇文章主要是了解一下内核中的经常出现的宏container_of,这个宏定义在Linux驱动代码是很常见的。
比如:

有些宏经常出现还是得搞清楚底层是怎么写的,面试还可能会问到的。

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_head
    作为链表节点嵌入在 struct device 中,通过 container_of 可以从链表节点反推外层结构体。

设备驱动模型

在设备驱动中,通过回调函数的参数(如 struct device * 的某个成员)获取外层设备结构体。

4. 关键注意事项

依赖GCC扩展:

({ ... })为GCC的语句表达式,允许宏返回一个值。typeof是GCC扩展,非标准C特性。

安全性:

    • 必须确保ptr确实指向type结构体的member成员,否则导致未定义行为。
    • 不能用于位字段(无法取地址)。

5. 底层机制

  • 依赖编译器扩展:
typeof用于获取变量类型。({ ... })是 GCC 的语句表达式(Statement Expression),允许在宏中执行多步操作并返回结果。
  • 零指针技巧:offsetof 通过 (TYPE *)0获取成员偏移量,不实际访问地址 0。

6.container_of宏优点

container_of宏通过巧妙的指针运算和编译时类型检查,实现了从成员到父结构体的高效逆向查找。其核心优势在于:

  • 零开销:所有计算在编译时完成。
  • 类型安全:通过typeof和临时指针确保类型匹配。
  • 广泛适用:适用于需要灵活管理复杂数据结构的场景(如设备驱动、内核模块)。

7. 总结

container_of 是 Linux 内核中 “从成员反推父对象” 的核心工具,其本质是通过指针运算和类型转换实现地址计算。它体现了以下思想:

  • 松耦合设计:链表节点不依赖外层结构体的具体类型。
  • 高效的内存操作:避免冗余的结构体指针存储。
  • 类型安全:通过编译时检查减少运行时错误。

理解 container_of 是掌握 Linux 内核数据结构设计的关键一步。

谢谢阅读!

最新文章

随机文章