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源创计划”,欢迎正在阅读的你也加入,一起分享。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux内核container_of是一个宏定义,用于获取包含某个成员的结构体变量的地址。它的定义可以在include/linux/kernel.h找到。该宏的作用是通过给定的成员指针,返回包含该成员的结构体变量的地址。具体的定义如下: ```c #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) ``` 这个宏的使用非常方便,可以通过成员指针来获取整个结构体变量的地址。在进行内核驱动开发时,经常会用到这个宏来获取结构体变量的地址,以便进行相关操作。 #### 引用[.reference_title] - *1* [Linux内核:理解container_of宏](https://blog.csdn.net/qq_28877125/article/details/124209504)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Linux内核container_of的原理及其使用详解](https://blog.csdn.net/u010632165/article/details/107523477)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值