• Linux内核中container_of宏定义讲解


    在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宏定义的源码。

    1. // ptr 已知的字段的地址
    2. // type 结构体
    3. // member 已经字段的字段名
    4. #define container_of(ptr, type, member) ({ \
    5. const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    6. (type *)( (char *)__mptr - offsetof(type,member) );})
    7. // TYPE结构体
    8. // MEMBER已知字段的字段名
    9. #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. 再转换成结构体的指针来解释这段内存空间。

    总结:

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

  • 相关阅读:
    Microsoft Edge浏览器中使用免费的ChatGPT
    MySQL(10)视图
    JAVA练习题38:正则表达式基本练习
    一些数据库练习
    采用WPF开发第二版OFD阅读器,持续完善中,敬请期待!
    网络编程扩展
    自动驾驶坐标系与旋转矩阵的确定(位置补偿)
    [计算机毕业设计]食品安全数据的关联分析模型的应用
    asp毕业设计——基于asp+sqlserver的个人网站建设设计与实现(毕业论文+程序源码)——个人网站建设
    Apollo 应用与源码分析:CyberRT-日志配置
  • 原文地址:https://blog.csdn.net/qq_43799161/article/details/126223620