中航长江建设工程有限公司网站,如何用 ftp上传网站,去国外做外卖网站,石家庄市建设南大街小学网站我的圈子#xff1a; 高级工程师聚集地 我是董哥#xff0c;高级嵌入式软件开发工程师#xff0c;从事嵌入式Linux驱动开发和系统开发#xff0c;曾就职于世界500强企业#xff01; 创作理念#xff1a;专注分享高质量嵌入式文章#xff0c;让大家读有所得#xff01; … 我的圈子 高级工程师聚集地 我是董哥高级嵌入式软件开发工程师从事嵌入式Linux驱动开发和系统开发曾就职于世界500强企业 创作理念专注分享高质量嵌入式文章让大家读有所得 文章目录 1、container_of函数介绍2、container_of函数实现2.1 static_assert2.2 __same_type2.3 (type *)02.4 offsetof2.5 container_of 3、总结 Linux Version6.6 AuthorDonge Githublinux-api-insides 1、container_of函数介绍
container_of可以说是内核中使用最为频繁的一个函数了简单来说它的主要作用就是根据我们结构体中的已知的成员变量的地址来寻求该结构体的首地址直接看图更容易理解。 下面我们看看linux是如何实现的吧 2、container_of函数实现
/*** 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.** WARNING: any const qualifier of ptr is lost.*/
#define container_of(ptr, type, member) ({ \void *__mptr (void *)(ptr); \static_assert(__same_type(*(ptr), ((type *)0)-member) || \__same_type(*(ptr), void), \pointer type mismatch in container_of()); \((type *)(__mptr - offsetof(type, member))); })
函数名称container_of
文件位置include/linux/container_of.h
该函数里面包括了一些封装好的宏定义以及函数比如static_assert、__same_type、offsetof以及一些指针的特殊用法比如(type *)0)下面我们一一拆解来看。 2.1 static_assert
/*** static_assert - check integer constant expression at build time** static_assert() is a wrapper for the C11 _Static_assert, with a* little macro magic to make the message optional (defaulting to the* stringification of the tested expression).** Contrary to BUILD_BUG_ON(), static_assert() can be used at global* scope, but requires the expression to be an integer constant* expression (i.e., it is not enough that __builtin_constant_p() is* true for expr).** Also note that BUILD_BUG_ON() fails the build if the condition is* true, while static_assert() fails the build if the expression is* false.*/
#define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
#define __static_assert(expr, msg, ...) _Static_assert(expr, msg)函数名称static_assert
文件位置include/linux/build_bug.h
函数解析该宏定义主要用来 在编译时检查常量表达式如果表达式为假编译将失败并打印传入的报错信息
expr该参数表示传入进来的常量表达式...表示编译失败后要打印的错误信息_Static_assertC11中引入的关键字用于判断表达式expr并打印错误信息msg。
在container_of函数中主要用来断言判断 static_assert(__same_type(*(ptr), ((type *)0)-member) || __same_type(*(ptr), void) ,pointer type mismatch in container_of());2.2 __same_type
/* Are two types/vars the same type (ignoring qualifiers)? */
#ifndef __same_type
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#endif函数名称__same_type
文件位置include/linux/compiler.h
函数解析该宏定义用于检查两个变量是否是同种类型
__builtin_types_compatible_pgcc的内建函数判断两个参数的类型是否一致如果是则返回1typeofgcc的关键字用于获取变量的类型信息
了解完__same_type想要理解__same_type(*(ptr), ((type *)0)-member)需要先弄明白(type *)0的含义。 更多干货可见高级工程师聚集地助力大家更上一层楼 2.3 (type *)0
(type *)0该如何理解这个表达式呢
首先type是我们传入进来的结构体类型比如上面讲到的struct test而这里所做的可以理解为强制类型转换(struct test *)addr。addr可以表示内存空间的任意的地址我们在强制转换后默认后面一片的内存空间存储的是该数据结构。 而(type *)0的作用也就是默认将0地址处的内存空间转换为该数据类型。 我们就把0当作我们正常的addr地址变量来操作((type *)0)-member就是获取我们结构体的成员对象。((type *)0)-member是一种常见的技巧用于直接获取结构体type的成员member的类型而不需要定义一个type类型的对象。 2.4 offsetof
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) ((TYPE *)0)-MEMBER)
#endif函数名称offsetof
文件位置include/linux/stddef.h
函数解析该宏定义用于获取结构体中指定的成员距离该结构体偏移量。 TYPE表示结构体的类型MEMBER表示指定的结构体成员__builtin_offsetofgcc内置函数直接返回偏移量。 在新的linux源码中直接引用了gcc内置的函数而在老的内核源码中该偏移量的实现方式如下
#define offsetof(TYPE, MEMBER) ((size_t) ((TYPE *)0)-MEMBER)同样用到了((TYPE *)addr)上面我们知道
((TYPE *)addr)-MEMBER表示获取该结构体的成员((TYPE *)addr)-MEMBER)加了一个表示地址取该成员的内存地址。 比如我们addr0x00000010那么((TYPE *)0x00000010)-MEMBER)就相当于0x00000010size比如我们addr0那么((TYPE *)0)-MEMBER)就相当于size 到这里我们对container_of函数内部涉及的相关知识了然于胸下面我们再来看container_of简直容易到起飞。 2.5 container_of
#define container_of(ptr, type, member) ({ \void *__mptr (void *)(ptr); \static_assert(__same_type(*(ptr), ((type *)0)-member) || \__same_type(*(ptr), void), \pointer type mismatch in container_of()); \((type *)(__mptr - offsetof(type, member))); })static_assert断言信息避免我们传入的参数类型不对而做的编译检查处理直接忽略。
#define container_of(ptr, type, member) ({ \void *__mptr (void *)(ptr); \((type *)(__mptr - offsetof(type, member))); })offsetof(type, member)计算的是结构体中的成员的偏移量这里称为size (__mptr - offsetof(type, member))也就是根据我们已知的成员变量地址计算出来结构体的首地址 ((type *)(__mptr - offsetof(type, member)))最后强制转换为(type *)结构体指针。 比如我们已知的结构体成员的地址为0xffff0000计算之后如下 3、总结
linux内核中小小的一个函数内部包括的技巧如此之多static_assert、__same_type、(type *)0、offsetof。
了解完内部完整的实现手法之后我们也可以手码一个container_of了 欢迎关注 公号星球【嵌入式艺术】董哥原创