插件窝 干货文章 Linux内核基础篇——container_of原理和实际应用

Linux内核基础篇——container_of原理和实际应用

container struct 结构 指针 151    来源:    2025-04-14

Linux内核中的container_of宏:原理与应用详解

一、container_of宏概述

container_of是Linux内核中一个非常重要且巧妙的宏,它允许开发者通过结构体成员的指针来获取包含该成员的整个结构体的指针。这个宏在内核链表、设备驱动等场景中被广泛使用。

二、container_of宏的定义

在Linux内核源码中(通常位于include/linux/kernel.h),container_of宏的定义如下:

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

三、container_of工作原理

1. 宏参数说明

  • ptr: 结构体成员的指针
  • type: 包含该成员的结构体类型
  • member: 结构体中该成员的名称

2. 工作原理分解

  1. 类型安全检查:

    const typeof(((type *)0)->member) *__mptr = (ptr);
    

    这部分确保传入的ptr指针类型与结构体中member的类型匹配。

  2. 计算偏移量:

    offsetof(type, member)
    

    使用offsetof宏计算出membertype结构体中的偏移量。

  3. 指针转换:

    (char *)__mptr - offsetof(type, member)
    

    将成员指针转换为char*(便于字节级计算),然后减去偏移量得到结构体起始地址。

  4. 类型转换:

    (type *)(...)
    

    将计算出的地址转换为type类型的指针。

四、实际应用示例

1. 在内核链表中的应用

Linux内核链表实现是container_of最典型的应用场景:

struct list_head {
    struct list_head *next, *prev;
};

struct my_struct {
    int data;
    struct list_head list;
};

// 通过list_head获取包含它的my_struct
struct list_head *list_ptr = ...;
struct my_struct *parent = container_of(list_ptr, struct my_struct, list);

2. 在设备驱动中的应用

在字符设备驱动中,常用container_offile结构获取自定义设备结构:

struct my_device {
    struct cdev cdev;
    int reg;
    // 其他设备特定数据
};

static int my_open(struct inode *inode, struct file *filp)
{
    struct my_device *dev;
    dev = container_of(inode->i_cdev, struct my_device, cdev);
    filp->private_data = dev;
    // ...
}

3. 自定义结构体应用示例

#include <stdio.h>
#include <stddef.h>

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

struct student {
    int id;
    char name[20];
    float score;
};

int main() {
    struct student s = {1, "Alice", 90.5};
    float *score_ptr = &s.score;

    // 通过score成员获取整个student结构体
    struct student *s_ptr = container_of(score_ptr, struct student, score);

    printf("ID: %d, Name: %s, Score: %.1f\n", 
           s_ptr->id, s_ptr->name, s_ptr->score);
    return 0;
}

五、container_of的替代方案

虽然container_of非常有用,但在某些情况下可以考虑替代方案:

  1. 直接传递结构体指针:如果设计允许,直接传递结构体指针更直观
  2. C++的offsetof:在C++中可以使用类似的技术,但通常有更面向对象的解决方案

六、注意事项

  1. 类型安全:确保成员指针类型与结构体声明一致
  2. 性能container_of是编译时计算,没有运行时开销
  3. 可移植性:虽然标准C有offsetof,但container_of的完整实现依赖于GCC扩展
  4. 调试:错误使用可能导致难以调试的内存问题

七、总结

container_of宏展示了Linux内核开发者对C语言的深刻理解和巧妙运用。它通过指针运算和类型转换,实现了从成员到包含结构的反向引用,为内核数据结构的灵活管理提供了基础支持。理解这个宏的工作原理对于深入Linux内核开发和编写高效、灵活的C代码至关重要。