【Linux API 揭秘】container_of函数详解
【Linux API 揭秘】container_of函数详解
Linux Version:6.6
Author:Donge
Github:linux-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_assert:C11中引入的关键字,用于判断表达式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
函数解析:该宏定义用于检查两个变量是否是同种类型
__builtin_types_compatible_p:gcc的内建函数,判断两个参数的类型是否一致,如果是则返回1typeof:gcc的关键字,用于获取变量的类型信息
了解完__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
函数解析:该宏定义用于获取结构体中指定的成员,距离该结构体偏移量。

TYPE:表示结构体的类型MEMBER:表示指定的结构体成员__builtin_offsetof:gcc内置函数,直接返回偏移量。
在新的linux源码中,直接引用了gcc内置的函数,而在老的内核源码中,该偏移量的实现方式如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
同样用到了((TYPE *)addr),上面我们知道
((TYPE *)addr)->MEMBER:表示获取该结构体的成员&((TYPE *)addr)->MEMBER):加了一个&,表示地址,取该成员的内存地址。比如我们
addr=0x00000010,那么&((TYPE *)0x00000010)->MEMBER)就相当于0x00000010+size比如我们
addr=0,那么&((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了 :)

【Linux API 揭秘】container_of函数详解的更多相关文章
- Linux内核中container_of函数详解
http://www.linuxidc.com/Linux/2016-08/134481.htm
- Linux C 中 fork() 函数详解
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork() 函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同 ...
- Linux操作系统shell与函数详解
shell和函数的定义 1. linux shell 函数 将一组命令集或语句形成一个可用的块, 这些语句块称为函数. 2. shell 函数的组成 函数名:函数名字,注意一个脚本中函数名要唯一, ...
- (笔记)Linux下的ioctl()函数详解
我这里说的ioctl函数是指驱动程序里的,因为我不知道还有没有别的场合用到了它,所以就规定了我们讨论的范围.写这篇文章是因为我前一阵子被ioctl给搞混了,这几天才弄明白它,于是在这里清理一下头脑. ...
- [fork]Linux中的fork函数详解
---------------------------------------------------------------------------------------------------- ...
- Linux内核中kzalloc函数详解
**************************************************************************************************** ...
- Linux下的ioctl()函数详解
我这里说的ioctl函数是指驱动程序里的,因为我不知道还有没有别的场合用到了它,所以就规定了我们讨论的范围.写这篇文章是因为我前一阵子被ioctl给搞混了,这几天才弄明白它,于是在这里清理一下头脑. ...
- Linux系统调用--mmap/munmap函数详解【转】
转自:http://www.cnblogs.com/leaven/archive/2011/01/14/1935199.html http://linux.chinaunix.net/techdoc/ ...
- 【Linux 进程】fork函数详解
一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...
- Linux 系统 文件锁 fcntl函数详解
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int ...
随机推荐
- 推荐几款三维模型OBJ格式轻量化处理工具软件
推荐几款三维模型OBJ格式轻量化处理工具软件 以下是几款常用的三维模型OBJ格式轻量化处理软件的介绍: 1.MeshLab: MeshLab是一款免费且强大的开源三维模型处理软件,支持多种文件格式,包 ...
- Go 并发编程 - Goroutine 基础 (一)
基础概念 进程与线程 进程是一次程序在操作系统执行的过程,需要消耗一定的CPU.时间.内存.IO等.每个进程都拥有着独立的内存空间和系统资源.进程之间的内存是不共享的.通常需要使用 IPC 机制进行数 ...
- dotnet SemanticKernel 入门 自定义变量和技能
本文将告诉大家如何在 SemanticKernel 框架内定义自定义的变量和如何开发自定义的技能 本文属于 SemanticKernel 入门系列博客,更多博客内容请参阅我的 博客导航 自定义变量是一 ...
- 京东搜索EE链路演进
导读 搜索系统中容易存在头部效应,中长尾的优质商品较难获得充分的展示机会,如何破除系统的马太效应,提升展示结果的丰富性与多样性,助力中长尾商品成长是电商平台搜索系统的一个重要课题.其中,搜索EE系统在 ...
- 【译】.NET 8 拦截器(interceptor)
通常情况下,出于多种原因,我不会说我喜欢写关于预览功能的文章.我的大多数帖子旨在帮助人们解决他们可能遇到的问题,而不是找个肥皂盒或打广告.但是我认为我应该介绍这个 .NET 预览特性,因为它是我在 . ...
- 618京东到家APP-门详页反爬实战
一.背景与系统安全需求分析 1. 系统的重要性 上图所示是接口所属位置.对电商平台或在线商店而言,分类查商品都是很重要的,通过为用户提供清晰的商品分类,帮助他们快速找到所需产品,节省浏览时间,提升购物 ...
- Linux Ubuntu 安装Qt【安装完可以直接运行】
1.安装 Qt: 第一步:到官网http://download.qt.io/archive/qt/下载 Qt 安装包,此处本人安装的是 qt-opensource-linux-x64-5.7.0.ru ...
- iOS16新特性:实时活动-在锁屏界面实时更新APP消息
简介 之前在 <iOS16新特性:灵动岛适配开发与到家业务场景结合的探索实践> 里介绍了iOS16新的特性:实时更新(Live Activity)中灵动岛的适配流程,但其实除了灵动岛的展示 ...
- 小米云原生文件存储平台化实践:支撑 AI 训练、大模型、容器平台多项业务
小米作为全球知名的科技巨头公司,已经在数百款产品中广泛应用了 AI 技术,这些产品包括手机.电视.智能音箱.儿童手表和翻译机等.这些 AI 应用主要都是通过小米的深度学习训练平台完成的. 在训练平台的 ...
- MySQL实战实战系列 04 深入浅出索引(下)
在上一篇文章中,我和你介绍了 InnoDB 索引的数据结构模型,今天我们再继续聊聊跟 MySQL 索引有关的概念. 在开始这篇文章之前,我们先来看一下这个问题: 在下面这个表 T 中,如果我执行 se ...