很多人看内核源码都会懵:只拿到结构体里一个成员指针,怎么找到整个结构体?
答案就是两个核心宏: offsetof 算偏移, container_of 反推地址,也是内核“万物皆可挂链表”的精髓。
1. offsetof:算成员在结构体里的偏移
#define offsetof(TYPE, MEMBER)
((size_t)&((TYPE *)0)->MEMBER)
- 假装 0 地址有个结构体
- 取成员地址,数值就是偏移字节数
- 编译期计算,不访问真实内存
用途:知道成员离结构体开头有多远。
2. container_of:从成员反推整个结构体
#define container_of(ptr, type, member) \
(type *)((char *)ptr - offsetof(type, member))
公式:
结构体首地址 = 成员地址 − 成员偏移量
作用:
你拿到内嵌链表节点 list_head ,就能反推出它所属的进程、文件、设备结构体。
3. 内核为什么这么设计?
- 链表通用:一套 list_head 挂所有结构体
- 代码复用:不用为每种对象写一套链表
- 极高效:纯指针运算,无额外开销
- 实现“万物皆可挂链表”
4. 一句话记住
- offsetof :成员离结构体多远
- container_of :通过成员找到它爹
这就是 Linux 内核链表、驱动、对象管理最底层的魔法。
言简意赅,关注我吧!