linux内核中字体显示库,Linux内核中container_of的原理及其使用详解

前言

在进行内核驱动开发的时候,经常可以看到container_of的身影,其作用就是获取包含某个成员的结构体变量地址,函数原型如下所示;

#define container_of(ptr, type, member) ({ \

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

(type *)( (char *)__mptr - offsetof(type,member) );})

宏定义

container_of在Linux内核源码/linux/kernel.h中,其宏定义如下所示;

#ifndef offsetof

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#endif

/**

* 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) );})

如何使用

#include

#ifndef offsetof

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#endif

/**

* 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 human_mod{

int head;

char eye;

float foot;

};

int main(){

struct human_mod me;

struct human_mod *p;

me.head = 1;

me.eye = 2;

me.foot = 2;

p = (struct human_mod *)container_of(&me.head, struct human_mod, head);

printf("head:%d\r\n",p->head);

printf("eye :%d\r\n",p->eye );

printf("foot:%f\r\n",p->foot);

return 0;

}

测试结果如下所示;

b0805a3f4ddb2aa73203fa41593a5528.png

简单分析

typeof

在标准C中是不支持typeof语法的,在gnu扩展语法中可以支持;

typeof可以得到表达式的类型;

具体参考:https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html#C-Extensions

offsetof

这条语句的目的就是获取TYPE类型中MEMBER成员的相对偏移地址;

在C标准库中(stddef.h)也可以找到offsetof;由于在内核空间中没有标准C库,因此就出现了下面的宏定义;

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这样写会不会报错?

答案是不会,因为编译器只关心类型,只要不存在访问非法地址的内容就不会出现问题,那也是程序运行时的事情,而非编译时的事情;

也就是我们经常说的访问野指针,空指针,但是上述语句显然没有对指针指向的内存进行访问。

下面先简单测试一下:

#include

#ifndef offsetof

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#endif

int main(){

printf("head:%d\r\n",offsetof(struct human_mod , head));

printf("eye :%d\r\n",offsetof(struct human_mod , eye));

printf("foot:%d\r\n",offsetof(struct human_mod , foot));

return 0;

}

在这里面究竟发生了哪些事情;

简单解释一下,基本上是以下几个步骤;

1. 0

2. ((TYPE *)0)

3. ( ((TYPE *)0)->MEMBER )

4. &( ((TYPE *)0)->MEMBER )

5. ( (size_t) &( ((TYPE *)0)->MEMBER )

先将地址指向0;

强制转换成TYPE类型(这里的类型是由我们来输入的);

得到TYPE类型指针指向的MEMBER成员;

取出MEMBER成员的地址;

转换成size_t类型;

整体的过程,结合上面的代码,具体如下图所示;

cff5fa719f31a55864db80a75083178f.png

最终的运行结果;

运行结果如下:

cfb5085eb5b468e8ab0c6b870a8fa668.png

写在最后

当我们从这篇文章的开头开始查看这个宏定义的时候,甚至会怀疑第一行是否真的正确的。但事实证明,这样使用是正确的;

#define container_of(ptr, type, member) ({ \

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

(type *)( (char *)__mptr - offsetof(type,member) );})

第一行对于宏的结果并不是本质上重要的,但是它用于类型检查。

第二行真正的作用是什么?它从成员地址减去结构体成员的偏移量,从而得出结构体变量的实际地址。如此而已;

在最终剥离这些眼花缭乱的运算符,结构和技巧之后,整体就相对显得比较简单了。

本文同步分享在 博客“小麦大叔”(CSDN)。

如有侵权,请联系 support@oschina.cn 删除。

本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值