Linux 2.4.22
在这一版本中的 list_entry的宏定义实现如下:
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
乍一看,会觉得特别复杂,其实分析之后,会发现清晰明了:
实现背景:
linux 中 list 为了实现类似 c++ list模板的效果,即一个list可以适用于各种不同的struct。便采用类似下图的设计,使list 相当于一个绳索,可以挂载各种 struct。通过当前 list_head可以得到所挂载的struct指针,便实现了类似于模板效果。
如下实例
typedef struct page {
struct list_head list;
struct address_space *mapping;
unsigned long index;
...
};
page = list_entry(curr, struct page, list);
原理
实现通过当前 list_head得到所挂载的struct指针,便是借助于 list_entry。
#define list_entry(ptr, type, member) \
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
(unsigned long)(&((type *)0)->member)
用于算出 member 相对于 struct 的 offset。
因为C语言中可以对地址进行强制类型转换,不管这个地址的原始类型,也不管是否合法。这里首先对地址 0 进行强制类型转换,然后取变量member的地址。因为起始地址为0,这里的member的绝对地址等价于相对地址。类似的等价实现如下:
#define offsetof(type,field) ((char *) &((type *) 0)->field - (char *) 0)
-
((type *)((char *)(ptr)- offset))
ptr 减去 offset 即可得到原始指针的位置。
最新实现
但是在新的C语言规范中,对地址 0 进行强转会出现未定义行为,取而代之的是适用编译器实现的__builtin_offsetof
。
最新的linux source code 实现如下:
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
static_assert(__same_type(*(ptr), ((type *)0)->member) || \
__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER)
可以发现在最新的linux source code 中,list_entry 计算偏移的方式变为了__builtin_offsetof。文章来源:https://www.toymoban.com/news/detail-455825.html
参考:文章来源地址https://www.toymoban.com/news/detail-455825.html
- offsetof
- Does &((struct name *)NULL -> b) cause undefined behaviour in C11?
到了这里,关于linux中 list_entry 设计背景及原理解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!