`
bigfirebird
  • 浏览: 125059 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

container_of 解析

阅读更多
在学习Linux驱动的过程中,遇到一个宏叫做container_of。
该宏定义在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) ({                      \
const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
(type *)( (char *)__mptr - offsetof(type,member) );})

它的作用显而易见,那就是根据一个结构体变量中的一个域成员变量的指针来获取指向 整个结构体变量的指针。比如,有一个结构体变量,其定义如下:

struct demo_struct {
           type1 member1;
           type2 member2;
           type3 member3;
           type4 member4;
};     
struct demo_struct demo;
同时,在另一个地方,获得了变量demo中的某一个域成员变量的指针,比如:
      type3  *memp = get_member_pointer_from_somewhere();
此时,如果需要获取指向整个结构体变 量的指针,而不仅仅只是其某一个域成员变量的指针,我们就可以这么做:
     struct demo_struct *demop = container_of(memp, struct demo_struct, member3);
这样,我们就通过一个结构体变量的一个域成 员变量的指针获得了整个结构体变量的指针。
下面说一说我对于这个container_of的实现的理解:
首先,我们将 container_of(memp, struct demo_struct, type3)根据宏的定义进行展开如下:
      struct demo_struct *demop = ({                      \
         const typeof( ((struct demo_struct *)0)->member3 ) *__mptr = (memp);    \
         (struct demo_struct *)( (char *)__mptr - offsetof(struct demo_struct, member3) );})
其中,typeof是GNU C对标准C的扩展,它的作用是根据变量获取变量的类型。因此,上述代码中的第2行的作用是首先使用typeof获取结构体域变量member3的类型为 type3,然后定义了一个type3指针类型的临时变量__mptr,并将实际结构体变量中的域变量的指针memp的值赋给临时变量__mptr。

(char *)__mptr转换为字节型指针。(char *)__mptr - offsetof(type,member) )用来求出结构体起始地址(为char *型指针),然后(type *)( (char *)__mptr - offsetof(type,member) )在(type *)作用下进行将字节型的结构体起始指针转换为type *型的结构体起始指针。

假 设结构体变量demo在实际内存中的位置如下图所示:
     demo
+-------------+ 0xA000
|   member1   |
+-------------+ 0xA004
|   member2   |
+-------------+ 0xA010
|   member3   |
+-------------+ 0xA018
|   member4   |
+-------------+

则,在执行了上述代码的第2行之后__mptr的值即为0xA010。
再看上述代 码的第3行,其中需要说明的是offsetof,它定义在include/linux/stddef.h中,其定义如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
先分析一下这个 宏的运行机理:
一共4步
1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;
2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;
3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址;
4.(size_t)(&(((TYPE*)0)->MEMBER))结果转换类型。巧妙之处在于将0转 换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址;
同样,我们将上述的offsetof调用展开,即为:
(struct demo_struct *)( (char *)__mptr - ((size_t) &((struct demo_struct *)0)->member3) );
可见,offsetof的实现原理如上所述,就是取结构体中的域成员相对于地址0的偏移地址,也就是域 成员变量相对于结构体变量首地址的偏移。
因此,offsetof(struct demo_struct, member3)调用返回的值就是member3相对于demo变量的偏移。结合上述给出的变量地址分布图可知,offsetof(struct demo_struct, member3)将返回0x10。
于是,由上述分析可知,此 时,__mptr==0xA010,offsetof(struct demo_struct, member3)==0x10。
因此, (char *)__mptr - ((size_t) &((struct demo_struct *)0)->member3) == 0xA010 - 0x10 == 0xA000,也就是结构体变量demo的首地址(如上图所示)。
这就是从结构体某成员变量指针来求出 该结构体的首指针。指针类型从结构体某成员变量类型转换为该结构体类型。
由此,container_of实现了根据一个结构体变量中的一个域成员 变量的指针来获取指向整个结构体变量的指针的功能。

以上内容载自网络,这篇文章分析的很透彻,顺便说一下,宋宝华的《linux设备驱动 开发详解》P132 最后一行当中对该宏的参数解释是错误的!当然了,暇不掩瑜!
以下是我自己的一些理解:
首先,我定义了一个字符设备结 构体
struct    globalmem_dev
{
    struct cdev   my_cdev;            //字符设备之基础结构体
    unsigned char mem[GLOBALMEM_SIZE];   
    struct semaphore sem;/
};
接下来我实例化了一个该设备的指针对象
struct globalmem_dev    *pdev;

后来在open函数中我是这么来用的
int globalmem_open(struct inode *inode, struct file *filp)关于filp的产生和消亡参见《驱动详解》P92
{                                  
    struct globalmem_dev *pdev;   
    printk("\nFunction globalmem_open Invoked\n");   
    pdev = container_of(inode->i_cdev,  struct   globalmem_dev,  my_cdev);
    filp->private_da
ta = pdev;   
    if(down_trylock(&pdev->sem))//获得信号量,  真的我爱  container_of!!!!  我爱死container_of 了!!!
        return -EBUSY;    
    return 0;
}
对 以上用法的说明:
参数3是参数2这个结构体的一个成员的名字!而不是类型名!参数1是一个指针,它指向参数3这个成员
inode中的i_cdev字段是一个指针,当我们成功insmod了一个设备驱动的时候,我们会通过mknod创建一个设备文件节点并和具体设备 (驱动)想关联,这个设备文件节点所对应的就是struct inode结构体的一个实例,这个结构体有一个字段i_cdev,是个struct   cdev类型的指针,它会指向设备结构体的my_cdev字段。至此你已经有了一个指向某个 globalmem_dev的my_cdev字段的一个指针(在调用open前pdev的内存分配假定已经完成)由此container_of可以帮你计 算出指向该设备结构体的指针。
当一个设备驱动对应多个设备(子设备)时,你就知道container_of发挥的作用了!当你针对每一个设备调用open时,因为每个设备对应的设备文 件节点不一样,那么根据该节点的i_cdev字段所计算的设备结构体指针也不一样,你就可以找到特定节点所对应的设备结构体!而不至于对不同的子设备编写 大同小异的各自的设备驱动。
分享到:
评论

相关推荐

    unity解析json对象

    So, I've written a very simple JSONObject class, which can be generically used to encode/decode data into a simple container. This page assumes that you know what JSON is, and how it works.

    container-ioc:由Typescript驱动的Java和Node.js应用程序的控制容器反转和依赖注入

    例子:安装: npm install --save container-ioc基本: 下面的代码示例用Typescript编写。 查看以获取使用Javascript编写的示例。步骤1.定义您的接口和类型。 类型的可能值: Symbol , string , Object 。

    adb1.0.26包含fastboot.exe

    INSTALL_FAILED_CONTAINER_ERROR 1. sdcard 访问失败; 2. 应用签名与 ROM 签名一致,被当作内置应用 1. 确认 sdcard 可用,或者安装到内置存储; 2. 打包时不与 ROM 使用相同签名 INSTALL_FAILED_INVALID_INSTALL_...

    OTL+代码+实例+帮助

    table container classes, generated from the template framework and the OTL-adapters. The OTL code gets expanded into direct database API function calls, so it provides very decent performance (only ...

    C#深入解析(2008)

    This event typically (such as with the Tomcat servlet container) results in a 500 response status code ("Internal Server Error"), so response.status evaluates to 500 instead of 200 ("Okay").

    VerifyStoreReceipt:此验证器解析并验证有效负载和 PKCS7 容器本身

    验证StoreReceiptiOS ... The payload of the PKCS7 container is encoded using ASN.1, as described by ITU-T X.690. 此验证器解析并验证有效负载和 PKCS7 容器本身。 感谢 Matthew Stevens 提出解析器代码。 感谢

    MediaInfo_GUI_19.09_Windows.exe

    Container: format, profile, commercial name of the format, duration, overall bit rate, writing application and library, title, author, director, album, track number, date, duration... Video: format, ...

    Leetcode回文串拼接-leetcode_note:用于记录leetcode题目的解析

    of Two Sorted Arrays 5.Longest Palindromic Substring 6.ZigZag Conversion 7.Reverse Integer 8.String To Integer 9.Palindrome Number 10.String To Integer 11.Container With Most Water 12.Integer To Roman...

    带注释的Bootstrap.java

    * starts the regular execution of the container. The purpose of this * roundabout approach is to keep the Catalina internal classes (and any * other classes they depend on, such as an XML parser) ...

    mdast-util-directive:mdast扩展,用于解析和序列化通用指令(

    扩展了和/或以支持( :cite[smith04] , ::youtube[Video of a cat in a box]{v=01ab2cd3efg} ,等)在。 解析( from-markdown )时,必须与结合使用。 有关语法的工作方式,请参见 。 该实用程序处理解析和序列...

    Flexslider左右按钮的jQuery图片切换插件 v2.1.rar

    // more complex call $('.flexslider').flexslider({ animation: "slide", controlsContainer: ".flex-container", start: function(slider) { $('.total-slides').text(slider.count); }, after: function(slider...

    ExtAspNet_v2.3.2_dll

    ExtAspNet - ExtJS based ASP.NET Controls with Full AJAX Support ExtAspNet是一组专业的Asp.net控件库,拥有原生的AJAX支持和丰富的UI效果, 目标是创建没有ViewState,没有JavaScript,没有CSS,没有...

    WebApiContrib.IoC.StructureMap:Web API的StructureMap依赖关系解析器

    WebApiContrib.IoC.StructureMap 这为您提供了连接StructureMap作为ASP.NET Web API的Inversion of Control容器所需的所有步骤。安装掌握的最佳方法是从NuGet那里获取软件包: Install-Package WebApiContrib.IoC....

    java经典面试2010集锦100题(不看你后悔)

    D) 出现IndexOutOfBounds的异常。 题目17:c 下面关于抽象类描述错误的是:(选择1项) A) 抽象类的关键字是abstract,在抽象类中可以定义变量和方法。 B) 抽象类中的方法如果可以有实现体,如果没有实现体,则该...

    Spring中文帮助文档

    13.5.1. 视图解析器(ViewResolver) 13.5.2. 视图解析链 13.5.3. 重定向(Rediret)到另一个视图 13.6. 本地化解析器 13.6.1. AcceptHeaderLocaleResolver 13.6.2. CookieLocaleResolver 13.6.3. ...

    Spring API

    13.5.1. 视图解析器(ViewResolver) 13.5.2. 视图解析链 13.5.3. 重定向(Rediret)到另一个视图 13.6. 本地化解析器 13.6.1. AcceptHeaderLocaleResolver 13.6.2. CookieLocaleResolver 13.6.3. ...

    ExtAspNet v2.2.1 (2009-4-1) 值得一看

    ExtAspNet v2.2.1 ExtAspNet是一组专业的Asp.net控件库,拥有原生的AJAX支持和丰富的UI效果, 目标是创建没有JavaScript,没有CSS,没有UpdatePanel,没有WebServices的Web应用程序。 支持的浏览器: IE 7.0+, ...

    remark-directive:备注插件以支持指令

    插件以支持( :cite[smith04] , ::youtube[Video of a cat in a box]{v=01ab2cd3efg}等)。 重要的! 这个插件是在加注的新的解析器发( ,见 )。 使用此插件可用于备注13+。 安装 : npm install remark-...

    DryIoc:DryIoc是用于.NET的快速,小型,功能齐全的IoC容器

    DryIoc是用于.NET的快速,小型,功能齐全的IoC容器 专为低礼仪使用,性能和可扩展性而设计。 支持:.NET 3.5 + 、. 1.0、1.3、2.0,PCL配置...冷启动-注册服务,然后打开范围并首次解析根范围的服务(例如,控制器

    DpdtInject:基于C#源生成器的高效编译时DI容器

    这是通过将巨大的解析逻辑转移到编译阶段到源代码生成器中来实现的。 作为附加概念,Dpdt不添加对发行版的引用,因此,如果您正在开发库/ Nuget程序包,则可以自由地将Dpdt用作DI容器。 您不会将DI强加给用户,所有...

Global site tag (gtag.js) - Google Analytics