在学习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字段所计算的设备结构体指针也不一样,你就可以找到特定节点所对应的设
备结构体!而不至于对不同的子设备编写大同小异的各自的设备驱动。

转自:http://dev.firnow.com/course/6_system/linux/linuxjq/20100313/198611.html

【转】container_of宏 分析的更多相关文章

  1. offsetof与container_of宏分析

    offsetof宏:结构体成员相对结构体的偏移位置 container_of:根据结构体成员的地址来获取结构体的地址 offsetof 宏 原型: #define offsetof(TYPE, MEM ...

  2. container_of宏定义分析---linux内核

    问题:如何通过结构中的某个变量获取结构本身的指针??? 关于container_of宏定义在[include/linux/kernel.h]中:/*_** container_of - cast a ...

  3. 内核中container_of宏的详细分析【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637597.html 内核中container_of宏的详细分析 16年2月28日09:00:37 内核中 ...

  4. container_of宏剖析

    container_of宏剖析//该宏位于include/linux/kernel.h 1.定义格式 /** * container_of - cast a member of a structure ...

  5. 对offsetof、 container_of宏和结构体的理解

    offsetof 宏 #include<stdio.h> #define offsetoff(type, member)      ((int)&((type*)0)->me ...

  6. Linux内核中container_of宏的详细解释

    上一节拒绝造轮子!如何移植并使用Linux内核的通用链表(附完整代码实现)我们在分析Linux内核链表的时候注意到内核在求解结构体偏移的时候巧妙的使用了container_of宏定义,今天我们来详细剖 ...

  7. linux中offsetof与container_of宏定义

    linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER)    ((size_t) &((TYPE *)0)->M ...

  8. (转)offsetof与container_of宏[总结]

    1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...

  9. offsetof与container_of宏[总结]

    1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...

随机推荐

  1. (转载)悟透JavaScript

    引子 编程世界里只存在两种基本元素,一个是数据,一个是代码.编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力. 数据天生就是文静的,总想保持自己固有的本色:而代码却天生活泼,总想改变这个 ...

  2. nginx配置静态资源关闭访问日志

    location ~ .*\.(css|js|gif|png|jpg|jpeg|bmp|swf)$ { root $root_path; access_log off; }

  3. MongoDB导入文件夹(内含json和bson文件)

    1. 使用mongo命令将数据库删除: mongo命令: use db_name; db.dropDatabase() 2. 导入(指定文件夹)数据: linux命令: mongorestore -d ...

  4. 【POJ - 3685】Matrix(二分)

    Matrix Descriptions 有一个N阶方阵 第i行,j列的值Aij =i2 + 100000 × i + j2 - 100000 × j + i × j,需要找出这个方阵的第M小值. In ...

  5. TypeError: __init__() got an unexpected keyword argument 'serialized_options'

    问题描述: TypeError: __init__() got an unexpected keyword argument 'serialized_options' File "objec ...

  6. mysql 插入数据后返回自增 ID 的七种方法

    参考地址:https://blog.csdn.net/qq_30715329/article/details/80868411 其中使用函数方式.存储过程方式.注解方式.xml属性方式设置都可. 常用 ...

  7. vue组件之间的通信, 父子组件通信,兄弟组件通信

    组件通讯包括:父子组件间的通信和兄弟组件间的通信.在组件化系统构建中,组件间通信必不可少的. 父组件--> 子组件 1. 属性设置 父组件关键代码如下: <template> < ...

  8. 什么是分析型数据库PostgreSQL版

    分析型数据库PostgreSQL版(原HybridDB for PostgreSQL)为您提供简单.快速.经济高效的 PB 级云端数据仓库解决方案.分析型数据库PostgreSQL版 兼容 Green ...

  9. 老贾的幸福生活day 04

    变量 变量名的规则: 变量名由字母,数字,下划线组成 变量名不能以数字开头 变量名要具有可描述性 变量名要区分大小写 变量名禁止使用python关键字 变量名不能使用中文和拼音 变量名推荐写法: 驼峰 ...

  10. 首篇-记录自己学习python之路!

    对于自己学习python的目的比较明确——爬虫和量化. 目前找了一些资源进行学习,先进行量化方面的学习,爬虫滞后.目前的目标是“180天掌握尽可能多的量化能力”! 以后定时发送自己学习思考内容以作自己 ...