Linux 中的 container_of 原理

源码基于:Linux 5.10

0.前言

container_of() 这个宏函数在Linux 内核中使用的频率还是很多的。网上关于 container_of 使用的优秀文章也很多,之所以笔者也写一篇,一是想更新下最新代码中的使用,二是融入些自己的拙见,方便自己回头查看,也希望能有助于后来读者。

1. 源码

include/linux/kernel.h

/**
 * 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) ({				\
	void *__mptr = (void *)(ptr);					\
	BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&	\
			 !__same_type(*(ptr), void),			\
			 "pointer type mismatch in container_of()");	\
	((type *)(__mptr - offsetof(type, member))); })

该宏函数主要用于通过一个结构体中成员的地址,转换获得该结构体的起始地址。

该宏函数有三个参数:

  • ptr:该成员变量的地址;
  • type:该成员是被包含在哪个结构体,也就是最终想要获取起始地址的结构体类型;
  • member:该成员在结构体中的名称,也就是ptr 指向的成员名称,与 ptr 是对应的;

该宏函数共做了三件事情:

  • 将第一个参数,即成员指针强转成 void*;
  • 调用 BUILD_BUG_ON_MSG() 进行编译assert,要求 ptr 确实是参数 member 对应的结构体成员,且 ptr 已经转换成 void*;
  • 调用 offsetof() 计算成员member 在结构体中的偏移量,进而计算出结构体的起始地址,并进行返回;

1.1 BUILD_BUG_ON_MSG()

include/linux/build_bug.h

/**
 * BUILD_BUG_ON_MSG - break compile if a condition is true & emit supplied
 *		      error message.
 * @condition: the condition which the compiler should know is false.
 *
 * See BUILD_BUG_ON for description.
 */
#define BUILD_BUG_ON_MSG(cond, msg) compiletime_assert(!(cond), msg)

当condition 为true 时,会退出编译并报错。

扩展后,可以看到 BUILD_BUG_ON_MSG() 为:

#define BUILD_BUG_ON_MSG(cond, msg)         \
	do {								\
		extern void __compiletime_assert_2(void)__attribute__((__error__(msg))); \
		if (!(condition))					\
			__compiletime_assert_2();				\
	} while (0)

其中 __compiletime_assert_N() 是gcc 定义断言函数,N 是通过 __COUNTER__ 计数得来的。

另外,想要调用这个 gcc 断言函数,需要定义 __OPTIMIZE__,即 gcc 编译的时候需要加上优化选项 -O<n>,n 是大于0的。

1.2 offsetof()

include/linux/stddef.h

#ifdef __compiler_offsetof
#define offsetof(TYPE, MEMBER)	__compiler_offsetof(TYPE, MEMBER)
#else
#define offsetof(TYPE, MEMBER)	((size_t)&((TYPE *)0)->MEMBER)
#endif

其中 TYPE 是结构体的类型,MEMBER 是成员名称。

当定义 __compiler_offsetof 时,调用 __compiler_offsetof() 宏函数,其实就是调用 gcc 的内置函数 __builtin_offsetof()。

当没有定义 __compiler_offsetof 时,通过 &((TYPE *)0)->MEMBER 获取成员变量的地址,0 是起始地址,那得到的成员变量的地址就是偏移量。

1.3 返回值

当通过 offsetof() 获取到偏移量之后,使用 ptr - offset 就是结构体的起始地址了。

之所以利用这种方式,是因为结构体的内存,在内存空间中是连续的。

结构体起始地址(也是第一个成员的首地址) 与成员N 相差 offset,container_of() 就是利用成员的首地址与offset,进而求出该结构体的起始地址。

2. 实例

drivers/dma-buf/heaps/system_heap.c

static void system_heap_buf_free(struct deferred_freelist_item *item,
				 enum df_reason reason)
{
	struct system_heap_buffer *buffer;
	struct sg_table *table;
	struct scatterlist *sg;
	int i, j;

	buffer = container_of(item, struct system_heap_buffer, deferred_free);

    ...
}

Linux 内核中使用 container_of() 的地方很多,这里用 dma-buf 中的一个函数为例。

通过上面源码分析,很容易理解此处:

通过结构体 system_heap_buffer 的成员变量 deffered_free 的地址 item,求得该结构体的起始地址。

 

结构体的源码如下: 

drivers/dma-buf/heaps/system_heap.c

struct system_heap_buffer {
	struct dma_heap *heap;
	struct list_head attachments;
	struct mutex lock;
	unsigned long len;
	struct sg_table sg_table;
	int vmap_cnt;
	void *vaddr;
	struct deferred_freelist_item deferred_free;

	bool uncached;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/242010.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

排序算法---希尔排序

1. 基本思想 希尔排序是插入排序的一种&#xff0c;它与直接插入排序不同的是&#xff0c;它会优先比较距离较远的元素&#xff0c;因此希尔排序又被称为“缩小增量排序”。希尔排序的实现思路是&#xff1a;先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序&…

VLAN协议与单臂路由

文章目录 VLAN协议与单臂路由一、VLAN的概念及优势1、分割广播域2、VLAN的优势3、VLAN数据帧 二、VLAN的种类1、静态VLAN2、动态VLAN3、VLAN划分方式 三、静态VLAN的配置1、VLAN的范围2、静态VLAN的配置2.1 配置静态VLAN的步骤2.2 vlan三种端口类型举例&#xff1a;配置静态VLA…

代码随想录算法训练营第四十四天 _ 动态规划_完全背包问题、518.零钱兑换II、377.组合总和IV。

学习目标&#xff1a; 动态规划五部曲&#xff1a; ① 确定dp[i]的含义 ② 求递推公式 ③ dp数组如何初始化 ④ 确定遍历顺序 ⑤ 打印递归数组 ---- 调试 引用自代码随想录&#xff01; 60天训练营打卡计划&#xff01; 学习内容&#xff1a; 完全背包问题 – 二维dp数组 动…

nvm 的使用 nvm 可以快速的切换 nodejs 的版本

nvm 是什么&#xff1f; nvm 是一个 node 的版本管理工具&#xff0c;可以简单操作 node 版本的切换、安装、查看。。。等等&#xff0c;与 npm 不同的是&#xff0c;npm 是依赖包的管理工具。 nvm 下载安装 安装之前需要先把 自己电脑上边的 node 给卸载了!!!! 很重要 下载地…

基于Java SSM框架实现个性化影片推荐系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现个性化影片推荐系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;个性化影片推荐系统当然也不能排除在外。个性化影片推荐系统是以实际运用…

【MySQL】:表的约束(上)

表的约束 一.非空约束二.default约束三.列描述四.zerofill五.主键1.单个主键2.复合主键 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性。比如有…

HCIA-WLAN V3.0,那些重点要点

一、WLAN各个标准&#xff0c;工作频段&#xff0c;理论速率。 二、OFDM和OFDMA&#xff0c;工作频段&#xff0c;空间流。 三、三种帧类型&#xff1a;管理帧、控制帧、数据帧&#xff0c;CAPWAP报文和端口。 四、帧间间隔&#xff0c;波束成形&#xff0c;信道绑定&#xff0…

【obs】官方最强插件obs-websocket入门

▒ 目录 ▒ &#x1f6eb; 导读需求开发环境 1️⃣ obs-websocket简介OBS版本说明obs-websocket版本说明安装&#xff08;27.x版本OBS&#xff09;配置插件 2️⃣ OBS-web介绍特征使用方法-5.xhttp vs https 3️⃣ obs-websocket-js开发tester.html 4️⃣ 其它开源项目obs-stud…

QML中Image动态显示图片内容

1.定义一个ColorImageProvider类 #ifndef COLORIMAGEPROVIDER_H #define COLORIMAGEPROVIDER_H#include <QObject> #include <QImage> #include <QQuickImageProvider>#include <QTimer>class ColorImageProvider :public QObject, public QQuickImag…

线上品牌展厅:打造数字品牌形象,助力品牌宣传

引言&#xff1a; 在数字化时代&#xff0c;随着互联网的普及和电子商务的发展&#xff0c;线上品牌展厅成为越来越多品牌关注的焦点。 一&#xff0e;什么是线上品牌展厅 1.线上品牌展厅的定义 线上品牌展厅是指通过互联网或移动应用程序等在线平台&#xff0c;展示品牌产品…

如何用postman进行http接口测试?

HTTP的接口测试工具有很多&#xff0c;可以进行http请求的方式也有很多&#xff0c;但是可以直接拿来就用&#xff0c;而且功能还支持的不错的&#xff0c;我使用过的来讲&#xff0c;还是postman比较上手。 优点&#xff1a; 1、支持用例管理 2、支持get、post、文件上传、…

python 安装对应版本的lxml

安装对应版本的lxml 先把对应版本的lxml文件下载下来&#xff0c;接着在文件夹路径输入cmd回车&#xff0c;用下面命令安装。

java设计模式-工厂方法模式

1.工厂方法(FactoryMethod)模式的定义 定义一个创建产品对象的工厂接口&#xff0c;将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。 2.工厂方法模式的主要优缺点 优点&#xff1a; 用户只需要知道具体工厂的名称…

数字海洋贸易:跨境电商的无边界冒险

数字时代的到来让商业舞台向全球开放&#xff0c;而跨境电商作为数字海洋中的一艘船&#xff0c;正在进行一场无边界的冒险。本文将深入探讨数字海洋贸易的概念&#xff0c;分析跨境电商在这个无边界环境中面临的挑战与机遇&#xff0c;以及如何在这个冒险中实现可持续的成功。…

【Java系列】详解多线程(二)——Thread类及常见方法(上篇)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一…

Git命令大全:从基础到高级应用

目录 一、增加/删除文件 1.1 添加文件到暂存区 1.2 添加所有文件到暂存区 1.3 从暂存区移除文件 1.4 从版本库和工作区删除文件 二、代码提交 2.1 提交暂存区文件到本地仓库 2.2 修改最后一次提交信息 三、本地分支 3.1 创建新分支 3.2 切换分支 3.3 创建并切换到新分支 3.4 删…

4G工业路由器物联网解决方案智慧储能系统

储能系统是用于电网和用户间起到电力缓冲和削峰填谷作用的电力管理平台。储能系统通常由电池、充电机、控制器、电能质量治理装置及监控系统组成。主要应用于可再生能源发电系统&#xff0c;电力需求侧响应&#xff0c;电动汽车充电等领域。 4G工业路由器是一款专门针对物联网…

Leetcode 46 全排列

题意理解&#xff1a; 首先明确全排列是什么&#xff1f; 使用集合里所有的元素&#xff0c;使用不同的顺序进行排列&#xff0c;所有的排列集合即为全排列。 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 这里的元素不会…

python程序编程代码大全,python编程代码详解

这篇文章主要介绍了python语言的代码书写规则有哪些&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 Python代码主要由&#xff1a;5个部分组成&#xff0c;下面就分别介绍&…

子目录文件夹图片汇总

import os import shutildef collect_images(source_folder, target_folder):# 遍历主文件夹及其所有子文件夹for root, dirs, files in