您的位置:首页 > 教程 > linux > Linux内核宏Container_Of的详细解释

Linux内核宏Container_Of的详细解释

2022-06-19 21:18:28 来源:易采站长站 作者:

Linux内核宏Container_Of的详细解释

目录1.结构体在内存中是如何存储的2.container_of宏3.typeof4.(((type*)0)-member)5.consttypeof(((type*)0)-memb...OX5站长之家-易采站长站-Easck.Com

目录
1. 结构体在内存中是如何存储的
2. container_of宏
3. typeof
4. (((type *)0)->member)
5. const typeof(((type * )0) ->member)*__mptr = (ptr);
6. offsetof(type, member))
7. (type * )((char * )__mptr - offsetof(type, member))
8. 举例

OX5站长之家-易采站长站-Easck.Com

1. 结构体在内存中是如何存储的

int main() 
{ 
 
 Student stu; 
 stu.id = 123456; 
 strcpy(stu.name,"feizhufeifei"); 
 stu.math = 90; 
 stu.PE = 80; 
 printf("Student:%p\r\n",&stu); 
 printf("stu.ID:%p\r\n",&stu.ID); 
 printf("stu.name:%p\r\n",&stu.name); 
 printf("stu.math:%p\r\n",&stu.math); 
 return 0; 
} 

打印结果如下:OX5站长之家-易采站长站-Easck.Com

//结构体的地址 
Student:0xffffcbb0 
//结构体第一个成员的地址 
stu.ID:0xffffcbb0  //偏移地址 +0 
stu.name:0xffffcbb4//偏移地址 +4 
stu.math:0xffffcbd4//偏移地址 +24 


??我们可以看到,结构体的地址和结构体第一个成员的地址是相同的。这也就是我们之前在拒绝造轮子!如何移植并使用linux内核的通用链表(附完整代码实现)中提到的为什么在结构体中要把 struct list_head放在首位。OX5站长之家-易采站长站-Easck.Com

不太理解的再看下这两个例子:OX5站长之家-易采站长站-Easck.Com

struct A { int a; char b; int c; char d; };a 偏移为 0 , b 偏移为 4 , c 偏移为 8 (大于 4 + 1 的 4 的最小整数倍), d 偏移为 12 。A 对齐为 4 ,大小为 16 。
struct B { int a; char b; char c; long d; };a 偏移为 0 , b 偏移为 4 , c 偏移为 5 , d 偏移为 8 。B 对齐为 8 , 大小为 16 。

Linux内核宏Container_Of的详细解释OX5站长之家-易采站长站-Easck.Com

我们可以看到,结构体中成员变量在内存中存储的其实是偏移地址。也就是说结构体A的地址+成员变量的偏移地址 = 结构体成员变量的起始地址。OX5站长之家-易采站长站-Easck.Com

因此,我们也可以根据结构体变量的起始地址和成员变量的偏移地址来反推出结构体A的地址。OX5站长之家-易采站长站-Easck.Com

OX5站长之家-易采站长站-Easck.Com

2. container_of宏

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER) 
#define container_of(ptr, type, member) ({          \ 
        const typeof(((type *)0)->member)*__mptr = (ptr);    \ 
    (type *)((char *)__mptr - offsetof(type, member)); }) 


??首先看下三个参数, ptr是成员变量的指针, type是指结构体的类型, member是成员变量的名字。OX5站长之家-易采站长站-Easck.Com

??container_of宏的作用是通过结构体内某个成员变量的地址和该变量名,以及结构体类型,找到该结构体变量的地址。这里使用的是一个利用编译器技术的小技巧,即先求得结构成员在结构中的偏移量,然后根据成员变量的地址反过来得出主结构变量的地址。下面具体分析下各个部分。OX5站长之家-易采站长站-Easck.Com

OX5站长之家-易采站长站-Easck.Com

3. typeof

首先看下typeof,是用于返回一个变量的类型,这是GCC编译器的一个扩展功能,也就是说typeof是编译器相关的。既不是C语言规范的所要求,也不是某个标准的一部分。OX5站长之家-易采站长站-Easck.Com

int main() 
{ 
 int a = 5; 
 //这里定义一个和a类型相同的变量b 
 typeof(a) b  = 6; 
 printf("%d,%d\r\n",a,b);//5 6 
 return 0; 
} 

OX5站长之家-易采站长站-Easck.Com

4. (((type *)0)->member)

((TYPE *)0) 将0转换为type类型的结构体指针,换句话说就是让编译器认为这个结构体是开始于程序段起始位置0,开始于0地址的话,我们得到的成员变量的地址就直接等于成员变量的偏移地址了。OX5站长之家-易采站长站-Easck.Com

(((type *)0)->member) 引用结构体中MEMBER成员。OX5站长之家-易采站长站-Easck.Com

typedef struct student{ 
 int id; 
 char name[30]; 
 int math; 
}Student; 
int main() 
{ 
 //这里时把结构体强制转换成0地址,然后打印name的地址。 
 printf("%d\r\n",&((Student *)0)->name);//4 
 return 0; 
} 

OX5站长之家-易采站长站-Easck.Com

5. const typeof(((type * )0) ->member)*__mptr = (ptr);

这句代码意思是用typeof()获取结构体里member成员属性的类型,然后定义一个该类型的临时指针变量__mptr,并将ptr所指向的member的地址赋给__mptr;OX5站长之家-易采站长站-Easck.Com

为什么不直接使用 ptr 而要多此一举呢?我想可能是为了避免对 ptr prt 指向的内容造成破坏。OX5站长之家-易采站长站-Easck.Com

OX5站长之家-易采站长站-Easck.Com

6. offsetof(type, member))

((size_t) &((TYPE*)0)->MEMBER) OX5站长之家-易采站长站-Easck.Com
OX5站长之家-易采站长站-Easck.Com

size_t是标准C库中定义的,在32位架构中被普遍定义为:OX5站长之家-易采站长站-Easck.Com

typedef unsigned int size_t; OX5站长之家-易采站长站-Easck.Com
OX5站长之家-易采站长站-Easck.Com

而在64位架构中被定义为:OX5站长之家-易采站长站-Easck.Com

typedef unsigned long size_t; OX5站长之家-易采站长站-Easck.Com
OX5站长之家-易采站长站-Easck.Com

可以从定义中看到,size_t是一个非负数,所以size_t通常用来计数(因为计数不需要负数区):OX5站长之家-易采站长站-Easck.Com

for(size_t i=0;i<300;i++) OX5站长之家-易采站长站-Easck.Com
OX5站长之家-易采站长站-Easck.Com

为了使程序有很好的移植性,因此内核使用size_t,而不是int,unsigned。((size_t) &((TYPE*)0)-&gt;MEMBER) 结合之前的解释,我们可以知道这句话的意思就是求出MEMBER相对于0地址的一个偏移值。OX5站长之家-易采站长站-Easck.Com

OX5站长之家-易采站长站-Easck.Com

7. (type * )((char * )__mptr - offsetof(type, member))

这句话的意思就是,把 __mptr 转换成 char* 类型。因为 offsetof 得到的偏移量是以字节为单位。两者相减得到结构体的起始位置, 再强制转换成 type 类型。OX5站长之家-易采站长站-Easck.Com

OX5站长之家-易采站长站-Easck.Com

8. 举例

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 
#define container_of(ptr, type, member) ({ \ 
        const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 
        (type *)( (char *)__mptr - offsetof(type,member) );}) 
         
typedef struct student 
{ 
 int id; 
 char name[30]; 
 int math; 
}Student; 
 
int main() 
{ 
    Student stu; 
        Student *sptr = NULL; 
  stu.id = 123456; 
  strcpy(stu.name,"zhongyi"); 
  stu.math = 90; 
        sptr = container_of(&stu.id,Student,id); 
        printf("sptr=%p\n",sptr); 
        sptr = container_of(&stu.name,Student,name); 
        printf("sptr=%p\n",sptr); 
        sptr = container_of(&stu.math,Student,id); 
        printf("sptr=%p\n",sptr); 
        return 0;  
} 


运行结果如下:OX5站长之家-易采站长站-Easck.Com

sptr=0xffffcb90 
sptr=0xffffcb90 
sptr=0xffffcbb4 


宏展开可能会看的更清楚一些OX5站长之家-易采站长站-Easck.Com

int main() 
{ 
    Student stu; 
        Student *sptr = NULL; 
  stu.id = 123456; 
  strcpy(stu.name,"zhongyi"); 
  stu.math = 90; 
  //展开替换 
        sptr = ({ const unsigned char  *__mptr = (&stu.id); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->id) );}); 
        printf("sptr=%p\n",sptr); 
        //展开替换 
        sptr = ({ const unsigned char  *__mptr = (&stu.name); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->name) );}); 
        printf("sptr=%p\n",sptr); 
        //展开替换 
        sptr = ({ const unsigned int *__mptr = (&stu.math); (Student *)( (char *)__mptr - ((size_t) &((Student *)0)->math) );}); 
        printf("sptr=%p\n",sptr); 
        return 0;  
} 

到此这篇关于Linux内核中Container_Of宏的详细解释的文章就介绍到这了,更多相关Linux内核中Container_Of宏内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

如有侵权,请联系QQ:279390809 电话:15144810328

相关文章

  • Linux下用GHOST来做系统备份

    Linux下用GHOST来做系统备份

    在Windows系统下备份我们可以用GHOST工具软件完成,Linux系统不能完全依赖于GHOST工具,一则是GHOST本身是有版权的软件,二是GHOST只支持ext2、ext3文件系统的Linux分区,不支持reiserfs、xfs等
    2019-10-25
  • 给 FreeBSD 12.1 安装 GNOME3 图形界面

    给 FreeBSD 12.1 安装 GNOME3 图形界面

    FreeBSD是一个完全开放的、安全的系统,可以Do it yourself的系统。但是个人还是不喜欢呆板的命令行界面,所有就给 FreeBSD 12.1 安装 GNOME3 图形界面。 开始操作,启动FreeBSD 12.1,以root身份
    2019-10-27
  • Ubuntu Studio一个不错的操作系统

    Ubuntu Studio一个不错的操作系统

    Ubuntu Studio 是一个基于 Ubuntu 而面向音频、视频及图形爱好者的操作系统。本次推出的 Ubuntu Studio 为 7.04 版,目前仅支持 Intel i386 兼容的处理器。 Ubuntu Studio 从大量的开源多媒体创作程序
    2019-10-25
  • 最受欢迎的10款Linux免费游戏

    最受欢迎的10款Linux免费游戏

    图为在Linux下运行的免费游戏America's Army 如果你认为 Linux下没有什么好游戏的话,那就错了!我们来看看Linux下最受欢迎的10款游戏吧,而且这些游戏都是完全免费的! # skyreal update(06-12
    2019-10-25
  • compiz fusion特效使用心得(附3D桌面图)

    compiz fusion特效使用心得(附3D桌面图)

    3D桌面图 LINUX发行版就是好,当大多数人还在沉迷于那些什么风格图标鸟玻璃效果甚至孜孜不倦津津乐道的时候,Linux的高手们已经悄悄为我们开启了一个桌面系统3D的时代。 当我第一次
    2019-10-25
  • Ubuntu 下aMule 的安装配置

    Ubuntu 下aMule 的安装配置

    aMule 是一个类似于 eMule 的多平台 P2P 客户端程序。以下将简略叙述 aMule 在 Ubuntu 中的安装及配置 [High ID] 与 [KAD] 的过程。 安装: sudo apt-get install amule sudo apt-get install amule-utils 如果需要
    2019-10-25
  • JMeter jp@gc - stepping thread group插件

    JMeter jp@gc - stepping thread group插件

    这是一个出单接口压力测试的小例子,了解一下压力测试最最基础的基础。出单接口是用来保险出单的,不需要在UI界面下一步下一步的出单,接口一调数据入库完事~~~。再啰嗦一句接
    2019-10-27
  • Ubuntu 下使用K3B软件刻录光盘(图)

    Ubuntu 下使用K3B软件刻录光盘(图)

    不知大家会不会在Ubuntu下刻录光盘呢?k3b是KDE环境下的一个CD/DVD刻录软件,有非常容易使用的界面,功能比较齐全。用起来感觉就象是win下的Nero一样简单自然,比ubuntu里原来的刻录CD方式
    2019-10-25