在Linux内核中经常能在list_head双向链表和socket等等操作中看到使用container_of宏定义来取到具体的结构体。
当然第一眼看到的时候就会觉得这是啥?这又是啥?这什么东西?为什么通过这个可以拿到结构体的基地址?
问题不大,笔者会用此文章+画图的方法来仔细讲解container_of宏定义。
那么,用一个列子+画图来理解把。
在struct-A结构体中,存在几个变量,包括struct-B。当我们已知struct-B的绝对地址时,此时需要得到struct-A的绝对地址,是不是就可以用struct-B的绝对地址 - struct-B前面的变量大小 = struct-A的绝对地址。 如下图所示:
此时,有一个很愚蠢的方法,就是硬生生的减去上面变量的大小,就可以得到struct-A的基地址,但是如果之前有很多很多变量呢?所以此方法肯定是行不通的。
而内核使用的是0地址的骚操作来得到前面的变量的大小,虚拟出一个从0开始的内存,在内存中放入struct-A结构体,得到struct-B的偏移量。此时struct-B的偏移量就是前面变量的总大小。
用struct-B的绝对地址 - struct-B在0地址的偏移量 = struct-A的基址。
我们再来看看container_of宏定义的源码。
- // ptr 已知的字段的地址
- // type 结构体
- // member 已经字段的字段名
- #define container_of(ptr, type, member) ({ \
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \
- (type *)( (char *)__mptr - offsetof(type,member) );})
-
-
-
- // TYPE结构体
- // MEMBER已知字段的字段名
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
container_of参数
// ptr 已知的字段的地址
// type 结构体
// member 已知的字段的字段名
offsetof参数
// TYPE 结构体
// MEMBER 已知的字段的字段名
最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!