定义
linux中的容器机制,是通过container_of宏来实现的。
// include/linux/stddef.h
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE, MEMBER) __compiler_offsetof(TYPE, MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#endif
// include/linux/kernel.h
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \
!__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
用途
根据指定对象的指定字段的指针,获取该指针对应的外部对象。
原理
对象内存布局与指针运算。

对象内存布局
所谓对象的内存布局,就是内存中对象各个字段的排列关系。这个关系在代码编译过程中已经确定。这种关系确定之后,对象中各个属性相对于对象的偏移量也就确定了。
要理解指针运算,首先要理解什么是指针。指针可以理解为虚拟内存地址中一个具体的值,这个值表示了数据在内存中的一个具体位置。指针运算,就是通过对某个指针进行加减操作,来确定一个其他的位置。
container_of宏定义了以下步骤来实现:
- 将传入的ptr参数解释成任意指针,为后续进行指针运算做准备。
- 将数字0转化为指定类型的指针,获取该指针对应的字段的值,将该值取地址后强制转换为size_t类型。
- 对ptr指针进行减法运算,而后强制类型转换为指定类型。
实际上,就是假定在虚拟内存地址0处有一个指定类型的对象,通过该对象可以获取到指定字段的偏移量。而后,对实际的指针减去偏移量,来获取到其对应的指定类型的对象。