【Orange Pi 5与Linux内核编程】-理解Linux内核中的container_of宏
理解Linux内核中的container_of宏
文章目录
- 理解Linux内核中的container_of宏
- 1、了解C语言中的struct内存表示
- 2、Linux内核的container_of宏实现理解
- 3、Linux内核的container_of使用
Linux 内核包含一个名为 container_of 的非常有用的宏。本文介绍了解 Linux 内核中的 container_of 宏。本文包括一个简单的程序,该程序说明了如何使用此宏,并解释了为什么它如此有用。
1、了解C语言中的struct内存表示
C 中的结构是 C 编程语言的一个强大功能。它们允许您通过将不同的变量分组到一个名称下来创建复杂的数据类型。那么,struct在内存中是如何表示的呢?例如,我们创建一个struct,并访问它的成员地址。
struct Student {int age;float grade;char name[50];int weight;
}__attribute__((packed));
请注意,为了方便演示这里使用了结构成员对齐。关于C语言的结构成员对齐,请参考:C语言结构成员对齐、填充和数据打包
那么,结构体Student在内存中的表示如下:

结构体的成员地址访问如下:
#include <stdlib.h>
#include <string.h>struct Student {int age;float grade;char name[50];int weight;
}__attribute__((packed));int main(int argc, char* argv[]) {struct Student st1;st1.age = 17;st1.grade = 7;st1.weight = 120;memset(st1.name, 0, sizeof(st1.name));memcpy(st1.name, "Jenson", 6);printf("sizeof struct = %d\n", sizeof(struct Student));printf("st1's address = %p\n", &st1);printf("st1.age = %p\n", &st1.age);printf("st1.grade = %p\n", &st1.grade);printf("st1.name = %p\n", &st1.name[0]);printf("st1.weight = %p\n", &st1.weight);printf("-------------------\n");printf("age'addr = %#lx\n", ((unsigned long)&st1));printf("grade'addr = %#lx\n", ((unsigned long)&st1 + sizeof(st1.age)));printf("name'addr = %#lx\n", ((unsigned long)&st1 + sizeof(st1.age) + sizeof(st1.grade)));printf("weight's addr = %#lx\n", ((unsigned long)&st1 + sizeof(st1.age) + sizeof(st1.grade) + sizeof(st1.name)));return 0;
}
我们将得到如下结果:
sizeof struct = 62
st1's address = 0x7fe806bb28
st1.age = 0x7fe806bb28
st1.grade = 0x7fe806bb2c
st1.name = 0x7fe806bb30
st1.weight = 0x7fe806bb62
-------------------
age'addr = 0x7fe806bb28
grade'addr = 0x7fe806bb2c
name'addr = 0x7fe806bb30
weight's addr = 0x7fe806bb62
如果结构体没有使用__attribute__((packed)),则name成员与weight成员将有两个字节的差异,因为编译器对结构体填充了两个字节,从而达到内存对齐的目的。
2、Linux内核的container_of宏实现理解
在Linux内核中,container_of宏的使用非常普遍,其实现如下:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)
#define container_of(ptr, type, member) ({ const typeof(((type *)0)->member)*__mptr = (ptr); (type *)((char *)__mptr - offsetof(type, member)); })
container_of宏接受3个参数:
- ptr – 指向成员的指针。
- type – 嵌入其中的容器结构的类型。
- member – 结构中成员的名称。
- 它返回成员的容器结构的地址。
为了方便理解,我们将Linux内核的container_of宏应用到用户空间程序中,示例代码如下:
#include <stdio.h>#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)( (char *)__mptr - offsetof(type,member) );})int main(void)
{struct sample {int mem1;char mem2;};struct sample sample1;printf("Address of Structure sample1 (Normal Method) = %p\n", &sample1);printf("Address of Structure sample1 (container_of Method) = %p\n", container_of(&sample1.mem2, struct sample, mem2));return 0;
}
因为我们已经知道结构sample1 的地址,那么,程序输出的地址应该相同还是不同? 让我们看看输出。
Address of Structure sample1 (Normal Method) = 0x7feeaf2598
Address of Structure sample1 (container_of Method) = 0x7feeaf2598
可以知道,示例程序输出的结果是相同的,那么,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.**/
#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)( (char *)__mptr - offsetof(type,member) );})
下面,我们将一步一步来解释这个宏是如何工作的?首先,让我们看第一行代码:
const typeof( ((type *)0)->member ) *__mptr = (ptr);
对于const关键字在这里就不做解释了。在这行代码中,我们对typeof感兴趣。
typeof()
typeof是非标准 GCC 扩展。GCC 扩展的类型,允许我们引用表达式的类型,并可用于声明变量。例如:
int x = 5;
typeof(x) y = 6;
printf("%d %d\n", x, y);
零指针引用
但是零指针取消引用呢?获取成员的类型是一个小小的指针魔术。它不会崩溃,因为表达式本身永远不会被计算。编译器关心的只是它的类型。如果我们要求回问地址,也会发生同样的情况。编译器同样不关心值,它只是将成员的偏移量添加到结构的地址中,在本例中为 0,并返回新地址。如下面代码所示:
struct s {char m1;char m2;
};/* This will print 1 */
printf("%d\n", &((struct s*)0)->m2);
#include <stdio.h>
int main(void)
{struct sample {int mem1;char mem2;};printf("Offset of the member = %d\n", &((struct s*)0)->mem2);return 0;
}
因此,我们从:
const typeof( ((type *)0)->member ) *__mptr = (ptr)
这行代码可以知道,它创建了局部常量指针变量。正如示例代码所实现的那样:
const typeof( ((type *)0)->member ) *__mptr = (ptr)—–> const char * __mptr = &sample1.mem2
接下来,让我们分析container_of宏的第二行代码:
(type *)( (char *)__mptr - offsetof(type,member) )
offsetof()宏
offsetof 是一个宏,它将成员的字节偏移量返回到结构的开头。宏如下所示:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
它甚至是标准库的一部分(可在 stddef.h 中找到)。
它返回一个名为 MEMBER 的成员的地址,该成员的类型为 TYPE 的结构,从地址 0(恰好是我们正在寻找的偏移量)存储在内存中。
现在我们需要根据我们的原始示例转换这个宏(offsetof(),container_of())。这些宏将取代我们的示例,如下所示。
container_of(&sample1.mem2, struct sample, mem2)||||\/
const char * __mptr = &sample1.mem2;
struct sample * ((char*) __mptr - &((struct sample*)0)->mem2)||||\/
const char* __mptr = 0x7FFD0D058784; //(Address of mem2 is 0x7FFD0D058784)
struct sample * (0x7FFD0D058784 - 4)||||\/
struct sample* (0x7FFD0D058780) //This is the address of the container structure
3、Linux内核的container_of使用
container_of宏在Linux内核中的使用示伪代码例如下:
struct foo {spinlock_t lock;struct workqueue_struct *wq;struct work_struct offload;(...)
};static void foo_work(struct work_struct *work)
{struct foo *foo = container_of(work, struct foo, offload);(...)
}static irqreturn_t foo_handler(int irq, void *arg)
{struct foo *foo = arg;queue_work(foo->wq, &foo->offload);(...)
}static int foo_probe(...)
{struct foo *foo;foo->wq = create_singlethread_workqueue("foo-wq");INIT_WORK(&foo->offload, foo_work);(...)
}
相关文章:
【Orange Pi 5与Linux内核编程】-理解Linux内核中的container_of宏
理解Linux内核中的container_of宏 文章目录 理解Linux内核中的container_of宏1、了解C语言中的struct内存表示2、Linux内核的container_of宏实现理解3、Linux内核的container_of使用 Linux 内核包含一个名为 container_of 的非常有用的宏。本文介绍了解 Linux 内核中的 contain…...
003.Linux SSH协议工具
我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈 入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈 虚 拟 环 境 搭 建 :👉&…...
web前端组织分析:深入剖析其结构、功能与未来趋势
web前端组织分析:深入剖析其结构、功能与未来趋势 在数字化浪潮的推动下,Web前端组织作为连接用户与数字世界的桥梁,其重要性日益凸显。本文将从四个方面、五个方面、六个方面和七个方面对Web前端组织进行深入分析,揭示其结构特点…...
GitCode热门开源项目推荐:Spider网络爬虫框架
在数字化高速发展时代,数据已成为企业决策和个人研究的重要资源。网络爬虫作为一种强大的数据采集工具受到了广泛的关注和应用。在GitCode这一优秀的开源平台上,Spider网络爬虫框架凭借其简洁、高效和易用性,成为了众多开发者的首选。 一、系…...
实现一个二叉树的前序遍历、中序遍历和后序遍历方法。
package test3;public class Test_A27 {// 前序遍历(根-左-右)public void preOrderTraversal(TreeNode root){if(rootnull){return;}System.out.println(root.val"");preOrderTraversal(root.left);preOrderTraversal(root.right);}// 中序遍…...
串扰(二)
三、感性串扰 首先看下串扰模型及电流方向: 由于电感是阻碍电流变化,受害线的电流方向和攻击线的电流方向相反。同时由于受害线阻抗均匀,故有Vb-Vf(感应电流属于电池内部电流)。 分析感性串扰大小仍然是按微分的方法…...
零基础入门学用Arduino 第四部分(三)
重要的内容写在前面: 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后,整体感觉是很好的,如果有条件的可以先学习一些相关课程,学起来会更加轻松,相关课程有数字电路…...
Mp3文件结构全解析(一)
Mp3文件结构全解析(一) MP3 文件是由帧(frame)构成的,帧是MP3 文件最小的组成单位。MP3的全称应为MPEG1 Layer-3 音频 文件,MPEG(Moving Picture Experts Group) 在汉语中译为活动图像专家组,特指活动影音压缩标准,MPEG 音频文件…...
ES 8.14 Java 代码调用,增加knnSearch 和 混合检索 mixSearch
1、pom依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>8.14.0</version></dependency><dependency><groupId>co.elastic.clients<…...
被腰斩的颍川郡守赵广汉
在颍川,他发明了举报箱,铁腕扫黑除恶。因为曾经在郡府所在地阳翟(禹州)当过县令,熟悉颍川社情民意,所以,任职郡守后雷厉风行,才不到一年,不但制服了骄横的豪门大族&#…...
【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 目录管理器(200分) - 三语言AC题解(Python/Java/Cpp)
🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 目录管理器(200分) 🌍 评测功能需要订阅专栏后私信联系清隆…...
关于自学\跳槽\转行做网络安全行业的一些建议
很好,如果你是被题目吸引过来的,那请看完再走,还是有的~ 为什么写这篇文章 如何自学入行?如何小白跳槽,年纪大了如何转行等类似问题 ,发现很多人都有这样的困惑。下面的文字其实是我以前的一个回答&#…...
计算机网络(1) OSI七层模型与TCP/IP四层模型
一.OSI七层模型 OSI 七层模型是国际标准化组织ISO提出的一个网络分层模型,它的目的是使各种不同的计算机和网络在世界范围内按照相同的标准框架实现互联。OSI 模型把网络通信的工作分为 7 层,从下到上分别是物理层、数据链路层、网络层、传输层、会话层、…...
认识QML
为什么使用Qt Quick? Qt4的设计用于满足开发者在主流桌面操作系统上有一套表现一致的窗口组件可以 使用。如今Qt的使用者面临了新的问题,他们需要提供可触碰交互的用户界面以满 足软件界面需求,并在主流桌面操作系统和移动操作系统上实现这些…...
llama-factory微调chatglm3
一、定义 案例/多卡 二、实现 案例 1. 下载chatglm3-6b-32k模型 2. 配置数据集微调指令 CUDA_VISIBLE_DEVICES0,1 llamafactory-cli train \--stage sft \--do_train True \--model_name_or_path /home/chatglm3-6b-32k \--finetuning_type lora \--template chatglm3 \--d…...
大文件上传实现
分片上传 将大文件分割成多个小片(chunk),逐个上传。每个片上传成功后,服务器可以返回确认信息。所有片上传完成后,服务器端将这些片重新组合成原始文件。 以下是一个简单的分片上传的前端实现示例: func…...
为何Proteus用户争相拥抱SmartEDA?揭秘背后的强大吸引力!
在电路设计与仿真领域,Proteus一度以其稳定性能和丰富功能赢得了众多用户的青睐。然而,近年来,越来越多的Proteus用户开始转向SmartEDA,这一新兴电路仿真软件正迅速崭露头角,成为行业内的翘楚。那么,究竟是…...
万界星空科技QMS质量管理介绍
产品的生产质量是企业发展之根本,对所有企业来说,建立完善质量控制体系,对企业生产经营以及发展竞争具有至关重要的影响,可以说是企业质量保证的防火墙。QMS质量管理系统对任何一家企业都具有重要意义,可帮助企业提高生…...
神经网络 torch.nn---nn.LSTM()
torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) LSTM — PyTorch 2.3 documentation LSTM层的作用 LSTM层:长短时记忆网络层,它的主要作用是对输入序列进行处理,对序列中的每个元素进行编码并保存它们的状态,以便后续的处理。 …...
Web前端JSP软件:深度解析与探索之旅
Web前端JSP软件:深度解析与探索之旅 在当今数字化时代,Web前端技术日新月异,JSP(Java Server Pages)软件作为其中的佼佼者,扮演着举足轻重的角色。本文将从四个方面、五个方面、六个方面和七个方面&#x…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...
轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
