深入浅出实例解析linux内核container_of宏

    做一件事情首先应该知道它的目的是什么。
    container_of的目的:如何通过结构中的某个变量获取结构本身的指针。
    总体思路:假想一下,你的结构体中有好几个成员,你如何通过里面的“任一成员”获取整个结构体的首地址呢。container_of的做法就是通过typeof定义一个与“任一成员”同类型的指针变量pvar_a(假设变量名就是pvar_a),并让指针变量pvar_a指向这个“任一成员”,然后用 “pvar_a的地址” 减去 “pvar_a相对于你的结构体的偏移量”,这个结果对应的地址其实就是你的结构体的首地址,最后将这个得到的结果强制转换为你的结构体对应的类型就OK了。
    关于container_of见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) );})
    注:补充一点C语言关于宏定义的知识,在一个宏中,如果有多条语句,则最终宏的返回结果是最后一条语句的值,这有点像逗号表达式。
    关于offsetof见stddef.h中:
    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
    TYPE是某struct的类型,(TYPE *)0是将0地址处强制转换为TYPE类型struct,MEMBER是该struct中的一个成员(你完全可以理解成就是我上面说的“任一成员”). 由于该struct的基地址为0,所以MEMBER的地址本身毫无疑问之意就是代表它自身的地址,另外它还巧妙的有另外一层含义就是MEMBER的地址数值上等于MEMBER相对与struct头地址的偏移量,现在你该明白之前为什么要选(TYPE *)0来强制转换,而不选(TYPE *)100,或者其他。
    关于typeof,这是gcc的C语言扩展保留字,用于声明变量类型.
    “const typeof( ((type *)0->member ) *__mptr = (ptr);” 的意思是声明一个与member同一个类型的指针常量 *__mptr,并初始化为ptr.
    “(type *)( (char *)__mptr - offsetof(type,member) );” 的意思是__mptr的地址减去member在该struct中的偏移量得到的地址, 再转换成type型指针. 其实该指针就是member的入口地址了.
    下面是我结合以前在网上的一个例子修改后的一个新例子,希望对读者有帮助:
#include<stdio.h>
/*
 *在应用层不能引用内核提供的container_of和offsetof宏,故需自己定义
 */
#define container_of(ptr, type, member) ({                      const typeof( ((type *)0)->member ) *__mptr = (ptr);             (type *)( (char *)__mptr - offsetof(type,member) );})
		 
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

/*
 *测试用的结构体
 */
struct student{
	char name[20];  
	char sex;
}stu={"liuhb569620660",‘M‘};

int main(int argc, char *argv[])
{
	struct student *stu_ptr; //存储container_of宏的返回值
	int offset;           	 //存储offsetof宏的返回值

	/*
	 *下面三行代码等同于 container_of(&stu.sex, struct student, sex )
	 */

	/*
	首先定义一个 _mptr指针, 类型为struct student结构体中sex成员的类型,typeof用于获取(((struct student*)0)->sex)的类型,此处该成员类型为char,((struct student*)0)在offsetof处讲解
	*/
	const typeof(((struct student*)0)->sex) *_mptr = &stu.sex;

	/*
	((struct student*)0)是把0地址强制转化为指向student结构体类型的指针,该指针从地址 0 开始的 21个字节 用来存放name与sex,(注:char name[20]与char sex共21字节),sex存放在第20个字节出(从0字节开始),&((struct student *)0)->sex 取出sex地址(此处即为20),并强制转化为整形,所以offset为20,后面的printf结果将证明这一点
	*/
	offset = (int)(&((struct student *)0)->sex);

	/*
	((char*)_mptr - offset)此处先把_mptr指针转化为字符形指针,(为什么这么做呢? 如果_mptr为整形指针 _mptr - offset 相当于减去 sizeof(int)*offset个字节),减去 offset值 相当于 得到_mptr所在结构体的首地址(即stu的地址),然后我们把 该地址 强制转化为 struct student 类型即可正常使用了
	*/
	stu_ptr = (struct student *)((char*)_mptr - offset);

	printf("\n### offsetof stu.sex = %d\n",offset);  
	printf("### stu_ptr->name = %s \n" 	"### stu_ptr->sex = %c\n\n", stu_ptr->name, stu_ptr->sex);

	/*
	下面增加验证直接调用container_of这个宏效果
	*/
	/*通过sex成员找到结构体首地址*/
	printf("\n### container_of(&stu.sex, struct student, sex)->name = %s\n", container_of(&stu.sex, struct student, sex)->name);  
	printf("### container_of(&stu.sex, struct student, sex)->sex = %c\n", container_of(&stu.sex, struct student, sex)->sex);  
	/*通过name成员找到结构体首地址*/
	printf("\n### container_of(&stu.sex, struct student, sex)->name = %s\n", container_of(stu.name, struct student, name)->name);  
	printf("### container_of(&stu.sex, struct student, sex)->sex = %c\n", container_of(stu.name, struct student, name)->sex);  

	return 0;
} 
    例子的演示效果如下:

深入浅出实例解析linux内核container_of宏,布布扣,bubuko.com

时间: 2024-10-11 05:16:07

深入浅出实例解析linux内核container_of宏的相关文章

嵌入式C语言自我修养 04:Linux 内核第一宏:container_of

4.1 typeof 关键字 ANSI C 定义了 sizeof 关键字,用来获取一个变量或数据类型在内存中所占的存储字节数.GNU C 扩展了一个关键字 typeof,用来获取一个变量或表达式的类型.这里使用关键字可能不太合适,因为毕竟 typeof 还没有被写入 C 标准,是 GCC 扩展的一个关键字.为了方便,我们就姑且称之为关键字吧. 通过使用 typeof,我们可以获取一个变量或表达式的类型.所以 typeof 的参数有两种形式:表达式或类型. int i ; typeof(i) j

深入解析Linux内核及其相关架构的依赖关系

Linux kernel 成功的两个原因: 灵活的架构设计使得大量的志愿开发者能够很容易加入到开发过程中:每个子系统(尤其是那些需要改进的)都具备良好的可扩展性.正是这两个原因使得Linux kernel可以不断进化和改进. 一.Linux内核在整个计算机系统中的位置 分层结构的原则: the dependencies between subsystems are from the top down: layers pictured near the top depend on lower la

解析 Linux 内核可装载模块的版本检查机制

转自:http://www.ibm.com/developerworks/cn/linux/l-cn-kernelmodules/ 为保持 Linux 内核的稳定与可持续发展,内核在发展过程中引进了可装载模块这一特性.内核可装载模块就是可在内核运行时加载到内核的一组代码.通常 , 我们会在两个版本不同的内核上装载同一模块失败,即使是在两个相邻的补丁级(Patch Level)版本上.这是因为内核在引入可装载模块的同时,对模块采取了版本信息校验.这是一个与模块代码无关,却与内核相连的机制.该校验机

Linux 内核常见宏定义

我们在阅读Linux内核是,常见到这些宏 __init, __initdata, __initfunc(), asmlinkage, ENTRY(), FASTCALL()等等.它们定义在 /include/linux/init.h 和 /include/linux/linkage.h 以及其他一些.h 文件中. 1. __init 位置:/include/linux/init.h 定义: #define __init   __attribute__ ((__section__ (".init.

深入解析Linux内核I/O剖析(open,write实现)

Linux内核将一切视为文件,那么Linux的文件是什么呢?其既可以是事实上的真正的物理文件,也可以是设备.管道,甚至还可以是一块内存.狭义的文件是指文件系统中的物理文件,而广义的文件则可以是Linux管理的所有对象.这些广义的文件利用VFS机制,以文件系统的形式挂载在Linux内核中,对外提供一致的文件操作接口. 从数值上看,文件描述符是一个非负整数,其本质就是一个句柄,所以也可以认为文件描述符就是一个文件句柄.那么何为句柄呢?一切对于用户透明的返回值,即可视为句柄.用户空间利用文件描述符与内

解析Linux内核的基本的模块管理与时间管理操作---超时处理【转】

转自:http://www.jb51.net/article/79960.htm 这篇文章主要介绍了Linux内核的基本的模块管理与时间管理操作,包括模块加载卸载函数的使用和定时器的用法等知识,需要的朋友可以参考下 内核模块管理Linux设备驱动会以内核模块的形式出现,因此学会编写Linux内核模块编程是学习linux设备驱动的先决条件. Linux内核的整体结构非常庞大,其包含的组件非常多.我们把需要的功能都编译到linux内核,以模块方式扩展内核功能. 先来看下最简单的内核模块 ? 1 2

全面解析Linux 内核 3.10.x - 如何开始

万事开头难 - 如何开始? 人总是对未知的事物充满恐惧!就像航海一样,在面对危难的时候,船员和船长是一样心中充满恐惧的!只是船员始终充满恐惧,而船长却能压抑恐惧并从当前找出突破口! 我没有船长之能,但也算入行两年的老船员,我会追随船长一起寻找突破口!而内核如此庞然大物不知从何入手这真的很正常,那么应该的入口在哪里?其实我也不知道,一千个读者就有一千个哈姆雷特.每个人都入口的理解都不一样,有人说是必须有着良好的C编程经验,有人说必须有着对Linux发行版等必要的操作经验,也有的人说一定要数据结构理

Linux内核--内核数据类型

转自:http://www.linuxidc.com/Linux/2013-12/93637.htm 将Linux 移植到新的体系结构时,开发者遇到的若干问题都与不正确的数据类型有关.坚持使用严格的数据类型和使用 -Wall -Wstrict-prototypes 进行编译可能避免大部分的 bug. -Wall 显示所有的警告 -Wstrict-prototypes 严格的检测原型,如果不一致,则出现警告 内核数据使用的数据类型主要分为3个类型: 标准C语言类型.确定大小的类型和特定内核对象的类

linux内核宏container_of

首先来个简单版本 1 /* given a pointer @ptr to the field @member embedded into type (usually 2 * struct) @type, return pointer to the embedding instance of @type. */ 3 #define container_of(ptr, type, member) 4 ((type *)((char *)(ptr)-(char *)(&((type *)0)->