container_of()宏

嵌入式驱动学习第三周——container_of()宏 在代码管理多个数据结构时,几乎总是需要将一个结构嵌入到另一个结构中,并随时检索它们,而不关心内存偏移或边界的问题。是一个宏函数,可以通过结构体成员的地址找到结构体的地址。ptr——结构体成员地址type——结构体类型member——结构体成员在结构体里的名字// 一个员工类int age;// 年龄char* name;// 姓名}Engineer;// 一个公司类// c艹工程师// java工程师// 员工人数} company; 阅读详情

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

【C语言】花式操作之container_of()宏的用法   前段时间在做算法移植的时候遇到一个问题,源头直指一个名为container_of()的函数,该函数的花式写法也着实让人不禁多看几眼。该函数的定义如下: #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof 阅读详情

相关推荐

On the convergence of FedAvg on non-iid data

On the convergence of FedAvg on non-iid data

Lyn_S的博客 7081

container_of()宏解析

在内核中很多地方都会使用到宏container_of()。         都知道宏container_of()的作用是:根据结构体中一个域成员变量的指针获取指向整个结构体的指针、现在来分析这个宏是如何实现的;         内核中container_of()代码如下: /** * container_of - cast a member of a structure out to th

渡江客涂鸦板 1187

深入浅出CenterFusion

结合论文和代码理解CenterFusion

Untitled的专栏 5104

container_of()一个在内核中经常使用的宏

假设ptr指向的成员变量的类型为T,__mptr就是一个指向T类型的指针。此外,container_of()宏使用了一个GCC的语言扩展"statement expression",即后面的{},可以在其中包含多条语句。在上面的程序中,pname指向stu的name成员,通过container_of()宏获得了指向整个struct student结构体的指针pstu,然后就可以访问id和name成员了。container_of()是一个在内核中经常使用的宏,用于获取一个结构体指针所在它所属的结构体的指针。

最后一个bug的博客 315

2025年12月11日 Coherent收发器下沉到数据中心:调研

技术可行性已基本具备。随着3nm DSP技术的成熟和Coherent-Lite等低功耗方案的推出,Coherent技术与IMDD的功耗差距在800G及以上速率节点快速缩小。特别是在1.6T及更高速率,两种技术的功耗开始趋于收敛。同时,通过系统级优化,如将DSP功能从可插拔模块转移到ASIC芯片,可实现30%以上的功耗降低。在成本方面,硅光技术的进步使得单通道硅光引擎BOM成本降至18美元,较2020年下降62%,逼近InP方案的经济临界点。光学系统机会丰富多样。

weixin_51184047的博客 1167

海思Hi3519AV100 sensor移植实战:IMX586驱动调试与硬件验证全流程

本文详细解析了在海思Hi3519AV100平台上移植索尼IMX586传感器的全流程实战。内容涵盖从硬件I2C通信验证、驱动代码移植与修改,到内核配置、时钟设定,以及使用示波器进行硬件信号深度验证和ISP图像调优等关键步骤,为开发者提供了一套清晰、可复现的sensor移植与调试方法。

weixin_29168393的博客 395

container_of() 宏的解析

文章目录container_of() 宏的解析定义作用原理分析ReferencesEnd container_of() 宏的解析 定义 其定义为: #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (pt...

Chase_to_the_remote的博客 381

OSPF规划两大模型:双塔奇兵、犬牙交错

大家好,今天给大家带来的是OSPF规划两大模型:双塔奇兵、犬牙交错,让我们直接开始吧! OSPF规划之双塔奇兵 说明: OSPF 如果某个区域存在两个 ABR,并且在两个 ABR 上都对 area n 内的路由做了聚合操作。 最佳配置: 两台ABR之间的红线应该使用以太网的子接口技术,一个子接口属于area0,另一个属于 area n;或者该链路规划为 area n,同时配置虚连接; ABR 的 loopback0 规划入 area 0 OSPF 规划之犬牙交错 说明: 有时接入层的设备会以乱序的

网络技术联盟站 2423

container_of()宏的简要解析 zz

在学习Linux驱动的过程中,遇到一个宏叫做container_of。该宏定义在include/linux/kernel.h中,首先来贴出它的代码:1.       439/** 2.       440 * container_of - cast a member of a structure out to the containing structure 3.       441 * @ptr:        the pointer to the member. 4.       442 * @type

crond123的专栏 508

ImportError: DLL load failed while importing _pydantic_core: 找不到指定的程序对应解决方案

本文主要介绍了ImportError: DLL load failed while importing _pydantic_core: 找不到指定的程序对应解决方案,希望能对使用Python的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 19万+

真正的工业级环形缓冲区(Circular Buffer)完整实现细节

永远不要永远不要ToArray()或AddRange()永远返回或两段Span永远用_head_tail% Capacity实现环形照着上面这套代码改,你的系统将彻底告别内存爆炸,从“实验室玩具”变成“产线战神”!需要我给你一个支持两段绘制的完整实现吗?

专注于前沿技术的探索与实践,热衷于从代码逻辑、架构设计到行业应用等多维度深挖技术本质。期待在技术之路上与你共同成长~ 127
上一篇: linux下 DMA管理
下一篇: 深入分析 Linux 内核链表
likeping
博客等级 码龄25年 41粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值