学无先后,达者为师

网站首页 编程语言 正文

Linux内核中container_of宏定义讲解

作者:程序员李哈 更新时间: 2022-08-13 编程语言

在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 已知的字段的字段名

  1.  先把已知字段的地址转换成具体的结构体中变量的类型(因为此方法在内核任意地方使用)。
  2. 把地址类型转换成char*来解释,因为offsetof算出来的是基于0地址的偏移量,所以必须要用char*来解释(因为地址-1,代表减去地址所对应的类型个大小的内存单元)。
  3. offsetof算出基于0地址的偏移量。
  4. 绝对地址 - 偏移地址 = 结构体的首地址。
  5. 再转换成结构体的指针来解释这段内存空间。

总结:

最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!

原文链接:https://blog.csdn.net/qq_43799161/article/details/126223620

栏目分类
最近更新