container_of
是Linux内核中一个非常重要且巧妙的宏,它允许开发者通过结构体成员的指针来获取包含该成员的整个结构体的指针。这个宏在内核链表、设备驱动等场景中被广泛使用。
在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)); })
ptr
: 结构体成员的指针type
: 包含该成员的结构体类型member
: 结构体中该成员的名称类型安全检查:
const typeof(((type *)0)->member) *__mptr = (ptr);
这部分确保传入的ptr
指针类型与结构体中member
的类型匹配。
计算偏移量:
offsetof(type, member)
使用offsetof
宏计算出member
在type
结构体中的偏移量。
指针转换:
(char *)__mptr - offsetof(type, member)
将成员指针转换为char*
(便于字节级计算),然后减去偏移量得到结构体起始地址。
类型转换:
(type *)(...)
将计算出的地址转换为type
类型的指针。
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);
在字符设备驱动中,常用container_of
从file
结构获取自定义设备结构:
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;
// ...
}
#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
是编译时计算,没有运行时开销offsetof
,但container_of
的完整实现依赖于GCC扩展container_of
宏展示了Linux内核开发者对C语言的深刻理解和巧妙运用。它通过指针运算和类型转换,实现了从成员到包含结构的反向引用,为内核数据结构的灵活管理提供了基础支持。理解这个宏的工作原理对于深入Linux内核开发和编写高效、灵活的C代码至关重要。