linux内核 container_ofC语言之应用

之前在剖析内核链表的文章中就有说到这个 container_of宏展开后的应用技巧。


//offset(struct list , list);----->展开后((size_t) & (struct list *)0 -> list)

//写清楚一点时这样:

//struct list *p = NULL ; &p->list ; 即是求p这个结构体指针的成员list的地址,只不过p是0

//地址,从0地址开始计算list成员的地址,也就是成员list在结构体struct list中的偏移量

//这个宏的作用就是求解MEMBER成员在TYPE中的偏移量

//编译器不报错的原因是在编译阶段就已经知道结构体里每个成员的属性的相对偏移量了 ,

//在代码中对结构体成员的访问其实最终 会被编译器转化为对其相对地址的访问

//在代码运行期间,其实根本就没有变量名还有属性成员,有的也就只有地址。


//container_of最终结果返回的是第二个表达式的值,这里所谓的__mptr是一个中间的指针变量,其实就是list_head指针类型,被初始化为ptr

//而我所说的这个ptr,就是我们要求的list_head中的节点地址,定义这样一个中间的指针变量其实考虑了很多因素

//如果传参进来的是ptr++,会有副作用,就类似于(p++)+(p++)这样

//而(char *)__mptr之所以要强制转换为char实质上是因为地址是以字节为单位的,而char的长度是一个字节

//所以contain_of实质是两个地址相减的结果

//__mptr是结构体中list_head节点的地址,offset宏求的是list_head节点在MEMBER在结构体中TYPE中的偏移量,那么

//__mptr减去它所在结构体中的偏移量,就是结构体的地址了。
上代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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) );})
struct cona_t{
	int i;
	int j;
	int v;
	char t[10];
	unsigned short xy;
};

struct cona_t ct;
unsigned short xy;
int main(int argc,char * argv[])
{
	int xy;
	struct cona_t * p;
	memset(&ct,0,sizeof(struct cona_t));
	ct.i = ct.j = ct.v = 10;
	sprintf(ct.t,"%s","sdf");
	ct.xy = 20;
	p = container_of(&ct.xy,struct cona_t,xy);

	printf("%s\n",p->t);
	return 0;
}

运行结果:

从运行结果可以看出我们可以通过一个结构体指针去获取一个结构体本身的成员。

时间: 2024-10-01 20:13:51

linux内核 container_ofC语言之应用的相关文章

linux内核中的C语言常规算法(前提:你的编译器要支持typeof和type)

学过C语言的伙伴都知道,曾经比较两个数,输出最大或最小的一个,或者是比较三个数,输出最大或者最小的那个,又或是两个数交换,又或是绝对值等等,其实这些算法在linux内核中通通都有实现,以下的代码是我从linux内核源码的kernel.c中抠出来的代码,我们来看看: 我们直接上代码: #include <stdio.h> #include <stdlib.h> /* * min()/max() macros that also do * strict type-checking..

C语言之linux内核实现最大公约数算法

最大公约数算法,又称欧几里德算法,至今已有几千年的历史了.在我们开始学习C语言的时候最常用的算法就是辗转相除法,其实在linux内核中,内核也是使用这样的方法实现两数最大公约数的计算.      两个整数的最大公约数是能够同时整除它们的最大的正整数.辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的相除余数的最大公约数.       例如,252和105的最大公约数是21(252 = 21 × 12:105 = 21 × 5);       算法原理: 设两数为a.b(b<a)

C语言之linux内核实现位数高低位互换

linux内核实在是博大精深,有很多优秀的算法,我之前在工作中就遇到过位数高低位交换的问题,那时候对于C语言还不是很熟练,想了很久才写出来.最近在看内核的时候看到有内核的工程师实现了这样的算法,和我之前想的一样,那么今天就把它分享出来吧.       在开发需求中,有要实现32位.16位.8位数高低位交换的算法.那么我们具体看看代码实现: 还是一样,从linux内核中将代码抠出来: #include <stdio.h> //将一个8位数高低4位交换 static inline unsigned

linux内核select/poll,epoll实现与区别_C 语言

下面文章在这段时间内研究 select/poll/epoll的内核实现的一点心得体会: select,poll,epoll都是多路复用IO的函数,简单说就是在一个线程里,可以同时处理多个文件描述符的读写. select/poll的实现很类似,epoll是从select/poll扩展而来,主要是为了解决select/poll天生的缺陷. epoll在内核版本2.6以上才出现的新的函数,而他们在linux内核中的实现都是十分相似. 这三种函数都需要设备驱动提供poll回调函数,对于套接字而言,他们是

Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】

原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja  转自:http://blog.chinaunix.net/uid-25909619-id-4938390.html   在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段,      下面我就顺这代码逐个函数的解释,但是这

C语言之linux内核可变参实现printf,sprintf

      昨天,我发表了一篇用可变参实现的fprintf函数,其实说实话还不完全是可变参实现的,因为用到了FILE * 这样的指针,需要包含stdio.h这个头文件才能实现这个函数,今天我们就来看看,如何抛弃stdio.h,全0开始实现printf , sprintf ,当然,这段代码是我在linux内核里面获取的,再经过我本人修改,移植,在DevC++这个编译环境中通过测试.我们来看看代码:       #include <stdarg.h> #define NULL 0 //如果字符串中

C语言在linux内核中do while(0)妙用之法

为什么说do while(0) 妙?因为它的确就是妙,而且在linux内核中实现是相当的妙,我们来看看内核中的相关代码: #define db_error(fmt, ...) \ do { \ fprintf(stderr, "(error): "); \ fprintf(stderr, fmt, ##__VA_ARGS__); \ } while (0) 这只是个普通的调试信息的输出,有人便会认为,你这不是多此一举吗?去掉do while(0)不一样也实现了吗?其实不然,我们看看例子

C语言之linux内核实现平方根计算算法

关于平方根的计算,在linux内核中也有实现,就像math.h数学库里的sqrt这个函数一样.       平方根的公式定义: 如果一个非负数x的平方等于a,即    ,    ,那么这个非负数x叫做a的算术平方根.a的算术平方根记为    ,读作"根号a",a叫做被开方数(radicand).求一个非负数a的平方根的运算叫做开平方.结论:被开方数越大,对应的算术平方根也越大(对所有正数都成立). 一个正数如果有平方根,那么必定有两个,它们互为相反数.显然,如果我们知道了这两个平方根的

linux内核 进程地址空间

问题描述 linux内核 进程地址空间 大神: 我是一个Linux内核菜鸟,在<<深入linux内核架构>>一书中写道:"各个进程的地址空间都是独立的", ,大神, 我不理解进程的"地址空间"是什么, 是进程的内存吗,请赐教,不胜感激涕零. 解决方案 进程启动后,都有一块自己的私人空间,其它进程 不可以直接访问的 .基本上所有操作系统都是这样的,这是最基本的安全性.这里所说的地址空间 就是一系列的内存地址罢了. 解决方案二: 内容是从<