记录一下学习 linux 源码过程中,遇到的一些神奇 coding 技巧
linux 版本为 v6.18
container_of
在 include/linux/types.h 里面定义了这样一个奇怪的东西:
1 | struct list_head { |
容易看出来这是定义了一个双向链表的节点,但是奇怪的点在于每一个节点除了前驱和后继之外没有存任何的信息,那这个双向链表有何作用呢?
查资料发现,内核中有一个叫做 container_of 的宏,这个宏的作用是从结构体的成员指针去访问对应的结构体,也即对于这样一个结构体:
1 | struct Foo { |
那利用 container_of, 就可以在知道成员变量 x 的地址 struct list_head * ptr 的情况下,得到 struct Foo 的地址,也就是实现了一个类型为 Foo 的双向链表!
由于 linux 内核是 C 而非 C++ 实现的,并没有原生的模板语法,因此这种设计的一种优势就是实现了泛型容器
具体来说,container_of 是利用了编译器提供的 __builtin_offsetof 函数,这个函数可以在编译器得知一个结构体成员相对于该结构体首地址的偏移量,于是就可以用成员的地址减去偏移量得到结构体的地址了
内核实现
1 |
其中:
ptr是成员指针(例子中的ptr)type是容器结构体(例子中的struct Foo)member是容器结构体中,对应成员的名字(例子中的x)
这里引出了一个新问题,也即当 ptr 是 const type * 的时候,转换后 const 属性会丢失,这会导致潜在的 bug,所以在 C11 引入 _Generic 语法之后就开始使用 container_of_const 了