Linux内核container_of宏解析:指针与链表应用疑问
关于Linux内核container_of宏的两个核心疑问解答
1. ((type *)0)->member 的作用
这个写法是编译期类型校验的手段,不会触发实际内存访问(仅做语法层面的类型推导,运行时不会真的读取地址0的数据)。
它的核心目的是配合static_assert,确保传入container_of的ptr指针,指向的成员类型和外层结构体type中member成员的类型完全匹配:
((type *)0)是把地址0强制转换成type类型的指针,相当于虚拟出一个type结构体实例在地址0处->member引用这个虚拟实例的member成员,编译器会推导出该成员的类型- 再通过
__same_type宏对比*(ptr)和这个成员的类型,不匹配就触发编译报错,避免传入错误类型指针导致后续地址计算出错
2. __mptr - offsetof(type, member) 的实现原理及链表场景应用
原理部分
offsetof(type, member)是编译器内置(或内核实现)的宏,用来计算member成员在type结构体中的字节偏移量(从结构体起始地址到该成员的字节数)__mptr是ptr转换后的void*指针,指向member成员的实际内存地址- 用成员地址减去它在结构体中的偏移量,得到的就是外层
type结构体的起始地址,再强制转换成type*类型,就拿到了外层结构体的指针
链表场景(以sk_buff为例)
Linux内核双向链表通过list_head结构体实现,它仅保存next、prev指针,不关联具体业务结构体。在sk_buff中,list是嵌入的list_head成员:
- 维护链表时,只需操作
list_head的指针(比如list_add、list_del等链表API),无需关心外层sk_buff - 当需要从链表节点反向获取
sk_buff时,就用container_of:假设拿到某个list_head的指针p_list,调用container_of(p_list, struct sk_buff, list),就会计算出该list成员对应的sk_buff起始地址,从而拿到完整的sk_buff指针,方便操作协议头、数据等其他成员
实际遍历示例:
struct list_head *pos; struct sk_buff *skb; list_for_each(pos, &skb_list_head) { skb = container_of(pos, struct sk_buff, list); // 此处可操作skb的其他成员,比如skb->data }
内容的提问来源于stack exchange,提问作者ca-hercor
相关产品推荐
相关产品推荐

