从嵌入的链表节点找回宿主对象
Linux 把 list_head 嵌入业务对象,通过成员偏移找回宿主,并在编译期检查指针类型。
工程问题#
通用链表只认识连接指针,调用者却需要操作完整对象;若为每个对象另建包装节点,会增加分配、间接访问和生命周期管理。
实际实现#
list_entry 调用 container_of:从成员指针减去 offsetof 得到宿主起址。static_assert 要求成员类型与指针指向类型相容,或允许 void 指针;这类检查只覆盖类型,不证明指针确实来自那个宿主对象。
处理流程#
- 在宿主结构中嵌入 list_head 成员
- 链表操作只维护这个成员的前后链接
- list_entry 把成员指针交给 container_of
- 编译期检查类型,运行时按成员偏移恢复宿主地址
源码片段#
固定版本 · 原文摘录10/**11 * container_of - cast a member of a structure out to the containing structure12 * @ptr: the pointer to the member.13 * @type: the type of the container struct this is embedded in.14 * @member: the name of the member within the struct.15 *16 * WARNING: any const qualifier of @ptr is lost.17 */18#define container_of(ptr, type, member) ({ \19 void *__mptr = (void *)(ptr); \20 static_assert(__same_type(*(ptr), ((type *)0)->member) || \21 __same_type(*(ptr), void), \22 "pointer type mismatch in container_of()"); \23 ((type *)(__mptr - offsetof(type, member))); })完整宏及原始注释:类型断言与 offsetof 地址恢复共同构成接口,原文同时说明 const 限定符会丢失。
594/**595 * list_entry - get the struct for this entry596 * @ptr: the &struct list_head pointer.597 * @type: the type of the struct this is embedded in.598 * @member: the name of the list_head within the struct.599 */600#define list_entry(ptr, type, member) \601 container_of(ptr, type, member)链表 API 直接调用 container_of,使嵌入的 list_head 能关联回任意指定的宿主类型。
两段来自同一固定内核版本:第一段给出约束与实现,第二段给出真实容器 API 的使用入口。
收益与取舍#
工程分析带来的收益
- 链表无需为每次挂接单独分配包装节点
- 同一对象可嵌入不同成员参与多条链表
- 常见的成员指针类型错误可提前暴露
付出的代价
- 宿主对象布局与容器相耦合;移动或释放对象前必须处理链接关系
- 同类型的错误成员、悬空指针和伪造地址不会被该类型检查识别
- 宏依赖内核所用编译器扩展,不能当作可移植 ISO C 模板
可比较的选择
外置包装节点对象无需携带链表字段,但引入额外节点与独立生命周期。
直接保存宿主指针可避免成员反推,却仍需单独的通用容器节点或索引空间。
版本与适用范围#
对应 Linux v6.12 的通用侵入式链表入口与宏定义。container_of 本身不分配内存、不加锁,也不延长对象生命周期;普通 container_of 会丢失 const,需保留限定符时可看同文件的 container_of_const。
参考来源#
源码、官方文档与原始论文include/linux/container_of.h · container_of / container_of_const
相关实现与比较#
4 个相关案例伙伴分配器把空闲物理页组织为不同 order 的连续块,并用空闲链表管理同阶块。
limit_req 把漏桶计算、红黑树查状态与队列回收组合起来,按配置的 key 管理请求速率。
QEMU 的 QEMUTimerList 用有序单链表维护定时器,链表头就是下一到期项。
缓存块命中后可能需要从队列中间移除,双向链表让这一步保持常数时间。