韦东山嵌入式linux系列-mmap

news2024/11/24 13:48:56

应用程序和驱动程序之间传递数据时,可以通过read、 write函数进行。这涉及在用户态 buffer 和内核态 buffer 之间传数据,如下图所示

应用程序不能直接读写驱动程序中的buffer,需要在用户态 buffer 和内核态 buffer 之间进行一次数据拷贝。这种方式在数据量比较小时没什么问题;但是数据量比较大时效率就太低了。比如更新 LCD 显示时,如果每次都让 APP传递一帧数据给内核,假设 LCD 采用 1024*600*32bpp 的格式,一帧数据就有1024*600*32/8=2.3MB 左右,这无法忍受。

改进的方法就是让程序可以直接读写驱动程序中的 buffer,这可以通过mmap 实现(memory map),把内核的 buffer 映射到用户态,让 APP 在用户态直接读写。

1.1 内存映射现象与数据结构

假设有这样的程序,名为 test.c:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int a;
int main(int argc, char **argv)
{
	if (argc != 2)
	{
		printf("Usage: %s <number>\n", argv[0]);
		return -1;
		}	
	a = strtol(argv[1], NULL, 0);
	printf("a's address = 0x%lx, a's value = %d\n", &a, a);
	while (1)
	{
		sleep(10);
	}
	return 0;
}

PC 上如下编译(必须静态编译):

gcc -o test test.c -staitc

分别执行 test 程序 2 次,最后执行 ps,可以看到这 2 个程序同时存在,这 2个程序里 a 变量的地址相同,但是值不同。如下图

观察到这些现象:
a) 2 个程序同时运行,它们的变量 a 的地址都是一样的: 0x6bc3a0;
b) 2 个程序同时运行,它们的变量 a 的值是不一样的,一个是 12,另一个是 123。
c) 疑问来了: 这 2 个程序同时在内存中运行,它们的值不一样,所以变量 a 的地址肯定不同;
d) 但是打印出来的变量 a 的地址却是一样的

怎么回事?

这里要引入虚拟地址的概念:CPU发出的地址是虚拟地址 , 它经过MMU(Memory Manage Unit,内存管理单元)映射到物理地址上,对于不同进程的同一个虚拟地址, MMU会把它们映射到不同的物理地址。如下图:

⚫ 当前运行的是app1时,MMU会把CPU发出的虚拟地址addr映射为物理地址paddr1,用paddr1 去访问内存。
⚫ 当前运行的是app2时,MMU会把CPU发出的虚拟地址addr映射为物理地址paddr2,用paddr2 去访问内存。
⚫ MMU 负责把虚拟地址映射为物理地址,虚拟地址映射到哪个物理地址去?
可以执行 ps 命令查看进程 ID,然后执行“ cat /proc/ID/maps”得到映射关系。(进程使用的虚拟地址空间)

这里是虚拟地址:物理地址 = 1:n,当然也可以,虚拟地址:物理地址 = n:1。合起来就是mmap

(1)get vaddr(应用程序获得一个应用程序可以使用的虚拟地址);

(2)get 物理地址paddr(获得这块内存的物理地址,需要做);

(3)映射map(虚拟地址和物理地址映射)。

每一个APP在内核里都有一个tast_struct,这个结构体中保存有内存信息:mm_struct。而虚拟地址、物理地址的映射关系保存在页目录表中,如下图所示:

解析如下:

⚫ 每个APP在内核中都有一个task_struct结构体,它用来描述一个进程;

⚫ 每个APP都要占据内存,在task_struct中用mm_struct来管理进程占用的内存;
        a) 内存有虚拟地址、物理地址, mm_struct中用mmap来描述虚拟地址,用pgd来描述对应的物理地址。
        b) 注意: pgd, Page Global Directory,页目录。

⚫ 每个 APP 都有一系列的 VMA: virtual memory

        a) 比如 APP 含有代码段、数据段、 BSS 段、栈等等,还有共享库。这些单元会保存在内存里,它们的地址空间不同,权限不同(代码段是只读的可运行的、数据段可读可写),内核用一系列的 vm_area_struct 来描述它们。
        b) vm_area_struct 中的 vm_start、 vm_end 是虚拟地址。
⚫ vm_area_struct 中虚拟地址如何映射到物理地址去?

        每一个 APP 的虚拟地址可能相同,物理地址不相同,这些对应关系保存在pgd 中。

1.2 ARM 架构内存映射简介

ARM 架构支持一级页表映射,也就是说 MMU 根据 CPU 发来的虚拟地址可以找到第 1 个页表,从第 1 个页表里就可以知道这个虚拟地址对应的物理地址。一级页表里地址映射的最小单位是 1M。

ARM 架构还支持二级页表映射,也就是说 MMU 根据 CPU 发来的虚拟地址先找到第 1 个页表,从第 1 个页表里就可以知道第 2 级页表在哪里;再取出第 2级页表,从第 2 个页表里才能确定这个虚拟地址对应的物理地址。二级页表地址映射的最小单位有 4K、 1K, Linux 使用 4K。

一级页表项里的内容,决定了它是指向一块物理内存,还是指向二级页表,如下图:

1 一级页表映射过程

一线页表中每一个表项用来设置 1M 的空间,对于 32 位的系统,虚拟地址空间有 4G, 4G/1M=4096。所以一级页表要映射整个 4G 空间的话,需要 4096个页表项。

第 0 个页表项用来表示虚拟地址第 0 个 1M(虚拟地址为 0~0xFFFFF)对应哪一块物理内存,并且有一些权限设置;

第 1 个页表项用来表示虚拟地址第 1 个 1M(虚拟地址为 0x100000~0x1FFFFF)对应哪一块物理内存,并且有一些权限设置;

依次类推。

使用一级页表时,先在内存里设置好各个页表项,然后把页表基地址告诉MMU,就可以启动 MMU 了。

以下图为例介绍地址映射过程:

a) CPU 发出虚拟地址 vaddr,假设为 0x12345678
b) MMU 根据 vaddr[31:20]找到一级页表项:
◆ 虚拟地址 0x12345678 是虚拟地址空间里第 0x123 个 1M,所以找到页表里第 0x123 项,根据此项内容知道它是一个段页表项。

段内偏移是 0x45678。

c) 从这个表项里取出物理基地址: Section Base Address,假设是0x81000000

d) 物理基地址加上段内偏移得到: 0x81045678

所以 CPU 要访问虚拟地址 0x12345678 时,实际上访问的是 0x81045678的物理地址。

2 二级页表映射过程

首先设置好一级页表、二级页表,并且把一级页表的首地址告诉 MMU。以下图为例介绍地址映射过程:
◼ CPU 发出虚拟地址 vaddr,假设为 0x12345678
◼ MMU 根据 vaddr[31:20]找到一级页表项:虚拟地址 0x12345678 是虚拟地址空间里第 0x123 个 1M,所以找到页表里第 0x123 项。根据此项内容知道它是一个二级页表项。
◼ 从这个表项里取出地址,假设是 address,这表示的是二级页表项的物理地址;
◼ vaddr[19:12]表示的是二级页表项中的索引 index 即 0x45,在二级页表项中找到第 0x45 项;
◼ 二级页表项格式如下:

里面含有这 4K 或 1K 物理空间的基地址 page base addr,假设是0x81889000:它跟 vaddr[11:0]组合得到物理地址: 0x81889000 + 0x678 =0x81889678。

所以 CPU 要访问虚拟地址 0x12345678 时,实际上访问的是 0x81889678的物理地址

1.3 怎么给APP新建一块内存映射

1 mmap调用过程

从上面内存映射的过程可以知道,要给 APP 新开劈一块虚拟内存,并且让它指向某块内核 buffer,我们要做这些事:

① 得到一个vm_area_struct,它表示 APP 的一块虚拟内存空间;很幸运 , APP调用mmap系统 函数时,内核就帮我们构造了一个vm_area_stuct 结构体。里面含有虚拟地址的地址范围、权限。
② 确定物理地址:
你想映射某个内核 buffer,你需要得到它的物理地址,这得由你提供。
③ 给 vm_area_struct 和物理地址建立映射关系:也很幸运,内核提供有相关函数。APP 里调用 mmap 时,导致的内核相关函数调用过程如下

addr是期望使用的虚拟地址。

2 cache 和 buffer

使用 mmap 时,需要有cache、 buffer的知识。下图是CPU 和内存之间的关系,有 cache、 buffer(写缓冲器) Cache 是一块高速内存;写缓冲器相当于一个 FIFO,可以把多个写操作集合起来一次写入内存。

程序运行时有“局部性原理”,这又分为时间局部性、空间局部性。
⚫ 时间局部性:
在某个时间点访问了存储器的特定位置,很可能在一小段时间里,会反复地访问这个位置。
⚫ 空间局部性:
访问了存储器的特定位置,很可能在不久的将来访问它附近的位置。

而 CPU 的速度非常快,内存的速度相对来说很慢。 CPU 要读写比较慢的内存时,怎样可以加快速度?根据“局部性原理”,可以引入 cache。

① 读取内存 addr 处的数据时:
a) 先看看 cache 中有没有 addr 的数据,如果有就直接从 cache 里返回数据:这被称为 cache 命中。

b) 如果 cache 中没有 addr 的数据,则从内存里把数据读入, 注意:它不是仅仅读入一个数据,而是读入一行数据(cache line)。

c) 而 CPU 很可能会再次用到这个 addr 的数据,或是会用到它附近的数据,这时就可以快速地从 cache 中获得数据

② 写数据:
a) CPU 要写数据时,可以直接写内存,这很慢;也可以先把数据写入cache,这很快。

b) 但是 cache 中的数据终究是要写入内存的啊,这有 2 种写策略:

        ⚫ 写通(write through):
                ◼ 数据要同时写入 cache 和内存,所以 cache 和内存中的数据保持一致,但是它的效率很低。能改进吗?可以!使用“写缓冲器”: cache大哥,你把数据给我就可以了,我来慢慢写,保证帮你写完。
                ◼ 有些写缓冲器有“写合并”的功能,比如 CPU 执行了 4 条写指令:写第 0、 1、 2、 3 个字节,每次写 1 字节;写缓冲器会把这 4 个写操作合并成一个写操作:写 word。对于内存来说,这没什么差别,但是对于硬件寄存器,这就有可能导致问题。
                ◼ 所以对于寄存器操作,不会启动 buffer 功能;对于内存操作,比如LCD 的显存,可以启用 buffer 功能。
        ⚫ 写回(write back):
                ◼ 新数据只是写入 cache,不会立刻写入内存, cache 和内存中的数据并不一致。
                ◼ 新数据写入 cache 时,这一行 cache 被标为“脏” (dirty);当cache 不够用时,才需要把脏的数据写入内存。

使用写回功能,可以大幅提高效率。但是要注意 cache 和内存中的数据很可能不一致。这在很多时间要小心处理:比如 CPU 产生了新数据, DMA 把数据从内存搬到网卡,这时候就要 CPU 执行命令先把新数据从 cache 刷到内存。反过来也是一样的, DMA 从网卡得过了新数据存在内存里, CPU 读数据之前先把cache 中的数据丢弃。

是否使用 cache、是否使用 buffer,就有 4 种组合(Linux 内核文件arch\arm\include\asm\pgtable-2level.h):

#define L_PTE_MT_UNCACHED	(_AT(pteval_t, 0x00) << 2)	/* 0000 */
#define L_PTE_MT_BUFFERABLE	(_AT(pteval_t, 0x01) << 2)	/* 0001 */
#define L_PTE_MT_WRITETHROUGH	(_AT(pteval_t, 0x02) << 2)	/* 0010 */
#define L_PTE_MT_WRITEBACK	(_AT(pteval_t, 0x03) << 2)	/* 0011 */

上面 4 种组合对应下表中的各项,一一对应(下表来自 s3c2410 芯片手册,高架构的 cache、 buffer 更复杂,但是这些基础知识没变):

是否启用 cache是否启用 buffer说明
00Non-cached, non-buffered (NCNB)
读、写都直达外设硬件
01Non-cached buffered (NCB)
读、写都直达外设硬件;
写操作通过 buffer 实现, CPU 不等待写操作完成, CPU 会
马上执行下一条指令
10Cached, write-through mode (WT),写通
读: cache hit 时从 cahce 读数据; cache miss 时已入一
行数据到 cache;
写:通过 buffer 实现, CPU 不等待写操作完成, CPU 会马
上执行下一条指令
11Cached, write-back mode (WB),写回
读: cache hit 时从 cahce 读数据; cache miss 时已入一
行数据到 cache;
写:通过 buffer 实现, cache hit 时新数据不会到达硬
件,而是在 cahce 中被标为“脏”; cache miss 时,通过
buffer 写入硬件, CPU 不等待写操作完成, CPU 会马上执行
下一条指令

第 1 种是不使用 cache 也不使用 buffer,读写时都直达硬件,这适合寄存器的读写。
第 2 种是不使用 cache 但是使用 buffer,写数据时会用 buffer 进行优化,可能会有“写合并”,这适合显存的操作。因为对显存很少有读操作,基本都是写操作,而写操作即使被“合并”也没有关系。
第 3 种是使用 cache 不使用 buffer,就是“write through”,适用于只读设备:在读数据时用 cache 加速,基本不需要写。
第 4 种是既使用 cache 又使用 buffer,适合一般的内存读写。

3 驱动程序要做的事
驱动程序要做的事情有 3 点:

① 确定物理地址

② 确定属性:是否使用 cache、 buffer

③ 建立映射关系

参考 Linux 源文件,示例代码如下:

还有一个更简单的函数:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6.1.面向对象技术-面向对象相关概念

基本概念 对象&#xff1a;基本的运行实体&#xff0c;为类的实例&#xff0c;封装了数据和行为的整体(数据和行为就是属性和方法)&#xff0c;如学生、汽车等真实存在的实体。对象具有清晰的边界、良好定义的行为和可扩展性。 学生有张三、李四等。张三就是具体的个体&#xf…

Java哈希算法

哈希算法 哈希算法1.概述2.哈希碰撞3.常用的哈希算法4.哈希算法的用途4.1校验下载文件4.2存储用户密码MD5加密5.SHA-1加密小结&#xff1a; 哈希算法 1.概述 哈希算法&#xff08;Hash&#xff09;又称摘要算法&#xff08;Digest&#xff09;&#xff0c;它的作用是&#xf…

OrangePi AIpro学习3 —— vscode开发昇腾DVPP程序

目录 一、VScode配置 1.1 下载和安装 1.2 安装和配置需要的插件 二、构建项目 2.1 项目架构 2.2 解决代码高亮显示 2.3 测试编译 2.4 总结出最简单的代码 2.5 vscode报错找不到头文件解决方法 三、代码简单讲解 3.1 初始化部分 3.2 拷贝数据到NPU显存中 3.3 准备裁…

Leetcode每日一题之仅仅反转字母(C++)

在学习之余对于知识的巩固也尤为重要&#xff0c;不论难度高低&#xff0c;都会对代码的理解有所加深&#xff0c;下面我们开始练习 思路解析 关于本题的核心思路就是如何判断字符串中元素是否为字母以及如何遍历字符串以达到仅反转的目的&#xff0c;这里用到的知识就是关于 s…

【数据结构与算法 | 二叉树篇】AVL树

1. 前言 AVL树是一种自平衡的二叉搜索树。为什么会出现AVL树。众所周知&#xff0c;虽然普通的二叉搜索树的平均时间复杂度为O(logn)&#xff0c;但最差的情况的时间复杂度为O(n)。为了避免最差的这种情况&#xff0c;出现了AVL树。 我们规定&#xff1a;如果树有个节点它的左…

大模型LLM——微调的七种方法

大模型的七种微调方法 文章目录 大模型的七种微调方法1. LoRA2. QLoRA3. 适配器调整4. 前缀调整5. 提示调整6. P-Tuning7. P-Tuning v2 1. LoRA LoRA的主要步骤包括&#xff1a; 选择微调目标权重矩阵&#xff1a;首先在大型模型&#xff08;如GPT&#xff09;中识别需要微调…

学习vue3 五,传送,缓存组件以及过渡和过渡列表

目录 Teleport传送组件 keep-alive缓存组件 transition动画组件 1. 过渡的类名 2. 自定义过渡class名 3. transition的生命周期 4.appear transition-group 1. 过渡列表 2. 列表的移动过渡 3. 状态过渡 Teleport传送组件 Teleport Vue 3.0新特性之一。 Teleport 是一…

AI技术如何重塑企业EHS安全健康环保体系,附实践案例

随着人工智能技术的快速发展&#xff0c;其在环境、健康和安全&#xff08;EHS&#xff09;管理领域的应用日益广泛。AI技术通过大数据分析、模式识别和预测建模等手段&#xff0c;为EHS管理提供了新的视角和工具。这一变革不仅提升了风险评估和事故预防的效率&#xff0c;同时…

Can‘t use Subversion command line client:svn不能使用Subversion命令行客户端:svn

1、导入idea中会报Can’t use Subversion command line client… 2、在提交svn代码的时候&#xff0c;出现这样的错误&#xff1a;Can’t use Subversion command line client: svn Probably the path to Subversion executable is wrong. Fix it. 问题原因&#xff1a;在安…

实验8-1-4 拆分实数的整数与小数部分

本题要求实现一个拆分实数的整数与小数部分的简单函数。 函数接口定义&#xff1a; void splitfloat( float x, int *intpart, float *fracpart );其中x是被拆分的实数&#xff08;0≤x<10000&#xff09;&#xff0c;intpart和fracpart分别是将实数x拆分出来的整数部分与…

sqli-labs1-24通关教程

目录 前置知识 第一关 1、第一关是单引号字符型注入,输入id1‘会报错 2、输入注释符正常显示 3、爆出列数为联合做准备 4、使用联合查询爆出数据库名 5、使用information_schema爆出表名 6、猜测用户名再users表中&#xff0c;爆出列名 ​编辑 7、利用查出来的表名数据…

【第2期】2024 搜索客 Meetup | Elasticsearch 的代码结构和写入查询流程的解读

本次活动由 搜索客社区、极限科技&#xff08;INFINI Labs&#xff09;联合举办&#xff0c;活动主题将深入探讨 Elasticsearch 的两个核心方面&#xff1a;代码结构以及写入和查询的关键流程。本次活动将为 Elasticsearch 初学者和有经验的用户提供宝贵的见解&#xff0c;欢迎…

叉车(工业车辆)安全监控管理系统,叉车安全方案

叉车作为特种设备的一种&#xff0c;存在一定的危险性&#xff0c;操作过程出现意外的情况可谓是不胜枚举&#xff0c;轻则伤财、受些皮外伤&#xff0c;重则直接致人死亡。为加强叉车操作的安全管理&#xff0c;从2023年12月1日起实施的TSG 81-2022《场&#xff08;厂&#xf…

【网络安全学习】SQL注入02:使用sqlmap进行注入

1.sqlmap的基本功能 sqlmap的基本功能是对Web应用中的数据库进行自动化的检测、利用和攻击。 使用pikachu靶场进行sqlmap的基础功能使用。 1️⃣ 第一步&#xff1a;先检查是否有注入点&#xff1a; # -u : 指定目标url&#xff0c;也就是指定注入点 sqlmap -u "http:…

终端命令行|CLI工具|CMD|PowerShell

基本概念 终端是一个文本界面&#xff0c;用于执行基于文本的程序。 果你正在运行任何用于 web 开发的工具&#xff0c;你一定需要打开命令行并运行一些命令来使用你所选择的工具 (这样的工具被称为CLI 工具也就是命令行接口工具)。 命令行接口工具 (CLI 工具) 是什么&#xf…

延伸--人工鱼群算法

目录 基本原理 改进策略 代码示例 应用实例 总结 人工鱼群算法在解决多峰函数问题中的具体应用案例和效果如何&#xff1f; 双群人工鱼群算法与传统人工鱼群算法相比&#xff0c;有哪些具体的改进和优势&#xff1f; 步长自适应调整在人工鱼群算法中的实现机制是什么&a…

vue3踩坑问题记录

//vue3element-plus //1、placeholder换行显示 const startTxt ref() const contentText ref<any>() startTxt.value "请描述问题内容、例如&#xff1a;" historyData.prompt.forEach((el:any)>{contentText.value \n${el.question}}) <ElInputv-mo…

点赞收藏测试文章(让我看看有多少机器人在刷互动量)

前言 这里有一些看似合理但实际毫无意义的句子&#xff0c;我需要用它来看看跟我互动的有多少机器人 如果你是人类用户&#xff0c;可以用评论来代替点赞&收藏&#xff0c;爱你~ 目录 前言 正文 1. 紫色的大象在夜空中游泳。 2. 月亮上的饼干师烤出了一片海洋。 3. 时…

数据结构(邓俊辉)学习笔记】词典 03—— 排解冲突(1)

文章目录 1. 一山二虎2. 泾渭分明3. 开放定址4. 线性试探5. 赖惰删除 1. 一山二虎 此前我们已经多次指出&#xff0c;对于需要动态维护的散列表冲突是不可避免的&#xff0c;无论你的散列函数设计的有多么精妙&#xff0c;因此我们不得不回答的第二个重要问题就是一旦发生冲突&…

苹果电脑维护工具:CleanMyMac X让你的Mac焕发新生!

在我们的数字生活中&#xff0c;苹果电脑&#xff08;Mac&#xff09;已成为不可或缺的一部分&#xff0c;无论是为工作披星戴月&#xff0c;还是为娱乐畅游云端。但是&#xff0c;就像任何长时间运行的机器一样&#xff0c;Mac也可能会因为积累的文件和不必要的数据而开始变慢…