在Linux中将设备驱动的地址映射到用户空间
本期主题:
MMU的简单介绍,以及如何实现设备地址映射到用户空间
往期链接:
- Linux内核链表
- 零长度数组的使用
- inline的作用
- 嵌入式C基础——ARRAY_SIZE使用以及踩坑分析
- Linux下如何操作寄存器(用户空间、内核空间方法讲解)
目录
- 0. 问题背景
- 1. MMU,虚拟内存
- 1. 存在的原因
- 2. 内存管理单元
- 2. 将设备地址映射到用户空间
- 1. 原理说明
- 2. 系统调用mmap例子
- 3. 驱动的mmap例子
0. 问题背景
在项目开发的过程中,遇到了需要 用户空间访问驱动里分配的数据的问题,趁机把这块知识补齐了一下,由于需要高频访问,所以 copy_to_user这种方案肯定是不可行的。
1. MMU,虚拟内存
1. 存在的原因
首先理解一下,为什么需要有虚拟内存这么一个概念,总结了以下好处:
- 进程隔离和内存保护,如果没有虚拟内存,假如A进程发生了地址访问问题,可能直接把不相关的B进程也影响到了,这样是不合理的。虚拟内存的方法就给每个进程提供了自己的虚拟空间,增加了系统的安全性和稳定性。
- 地址空间重用,物理内存地址是有限且连续的,而虚拟内存允许操作系统在物理内存中灵活地管理进程使用的地址空间。进程的虚拟地址空间可以是连续的,而实际的物理内存可以是不连续的,这种方式更高效地使用内存并减少碎片。
- 简化编程模型,使用虚拟内存,开发人员可以编写和管理程序而不必担心物理内存的实际大小和地址布局,这让开发变得更简单。
2. 内存管理单元
高性能处理器一般会提供一个内存管理单元MMU,该单元辅助操作系统进行内存管理,现代处理器常用的方案是 虚拟寻址方式(virtual addressing),参考下图:
简单介绍:
使用虚拟寻址时
- CPU会通过虚拟地址(virtual address, VA)来访问主存
- MMU进行地址翻译,把虚拟地址转换成物理地址
- 最后进行访问
2. 将设备地址映射到用户空间
1. 原理说明
一般情况下,用户空间是不能直接访问设备的寄存器或者地址空间的。
但是,可以在设备驱动程序中实现mmap()函数
,这个函数可以使得用户空间直接访问设备的地址空间。
mmap实现了这样的映射过程:
- 将用户空间的一段内存与设备内存关联
- 当用户访问用户空间的这段地址范围时,转换成对设备的访问
这种特性对显示一类的设备非常有意义,这样在用户空间就可以直接访问了,不需要从内核空间到用户空间的copy过程。
2. 系统调用mmap例子
写一个例子,mmap将文件映射到memory中去访问
例子:映射一个文件,并把内容打印出来
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char *argv[]) {// 检查是否传递了文件名if (argc < 2) {printf("Usage: %s <filename>\n", argv[0]);exit(EXIT_FAILURE);}const char *filename = argv[1];// 打开文件int fd = open(filename, O_RDONLY);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}// 获取文件大小struct stat sb;if (fstat(fd, &sb) == -1) {perror("fstat");close(fd);exit(EXIT_FAILURE);}// 使用 mmap 将文件映射到内存char *mapped = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}// 关闭文件描述符,文件已映射不需要再保持打开状态close(fd);// 打印文件内容for (off_t i = 0; i < sb.st_size; i++) {putchar(mapped[i]);}// 解除内存映射if (munmap(mapped, sb.st_size) == -1) {perror("munmap");exit(EXIT_FAILURE);}return 0;
}
实测:
3. 驱动的mmap例子
例子流程:
- 驱动里分配好空间
- 分配的空间,32PAGE,128KB,虚拟地址通过驱动的mmap file_operations映射出去
- 用户进程通过mmap直接访问这段空间,user1去改这段空间,usr2读取这段空间
代码:
driver:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/io.h>#define BUF_SIZE (32*PAGE_SIZE)static void *kbuff;static int remap_pfn_open(struct inode *inode, struct file *file)
{struct mm_struct *mm = current->mm;printk("client: %s (%d)\n", current->comm, current->pid);printk("code section: [0x%lx 0x%lx]\n", mm->start_code, mm->end_code);printk("data section: [0x%lx 0x%lx]\n", mm->start_data, mm->end_data);printk("brk section: s: 0x%lx, c: 0x%lx\n", mm->start_brk, mm->brk);printk("mmap section: s: 0x%lx\n", mm->mmap_base);printk("stack section: s: 0x%lx\n", mm->start_stack);printk("arg section: [0x%lx 0x%lx]\n", mm->arg_start, mm->arg_end);printk("env section: [0x%lx 0x%lx]\n", mm->env_start, mm->env_end);return 0;
}static int remap_pfn_mmap(struct file *file, struct vm_area_struct *vma)
{unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;unsigned long pfn_start = (virt_to_phys(kbuff) >> PAGE_SHIFT) + vma->vm_pgoff;unsigned long virt_start = (unsigned long)kbuff + offset;unsigned long size = vma->vm_end - vma->vm_start;int ret = 0;printk("phy: 0x%lx, offset: 0x%lx, size: 0x%lx\n", pfn_start << PAGE_SHIFT, offset, size);ret = remap_pfn_range(vma, vma->vm_start, pfn_start, size, vma->vm_page_prot);if (ret)printk("%s: remap_pfn_range failed at [0x%lx 0x%lx]\n",__func__, vma->vm_start, vma->vm_end);elseprintk("%s: map 0x%lx to 0x%lx, size: 0x%lx\n", __func__, virt_start,vma->vm_start, size);return ret;
}static const struct file_operations remap_pfn_fops = {.owner = THIS_MODULE,.open = remap_pfn_open,.mmap = remap_pfn_mmap,
};static struct miscdevice remap_pfn_misc = {.minor = MISC_DYNAMIC_MINOR,.name = "remap_pfn",.fops = &remap_pfn_fops,
};static int __init remap_pfn_init(void)
{int ret = 0;kbuff = kzalloc(BUF_SIZE, GFP_KERNEL);if (!kbuff) {ret = -ENOMEM;goto err;}ret = misc_register(&remap_pfn_misc);if (unlikely(ret)) {pr_err("failed to register misc device!\n");goto err;}pr_info("register misc device ok!\n");return 0;err:return ret;
}static void __exit remap_pfn_exit(void)
{misc_deregister(&remap_pfn_misc);kfree(kbuff);pr_info("deregister misc device ok!\n");
}module_init(remap_pfn_init);
module_exit(remap_pfn_exit);
MODULE_LICENSE("GPL");
usr:
//user1
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>#define PAGE_SIZE (4*1024)
#define BUF_SIZE (16*PAGE_SIZE)
#define OFFSET (0)int main(int argc, const char *argv[])
{int fd;void *addr = NULL;fd = open("/dev/remap_pfn", O_RDWR);if (fd < 0) {perror("open failed\n");exit(-1);}addr = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, OFFSET);if (!addr) {perror("mmap failed\n");exit(-1);}for (int i = 0; i < 16; i++) {for (int j = 0; j < 10; j++) {*(uint8_t *)((uintptr_t)addr + i*PAGE_SIZE + j) = j;}}return 0;
}
//user2
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>#define PAGE_SIZE (4*1024)
#define BUF_SIZE (16*PAGE_SIZE)
#define OFFSET (0)int main(int argc, const char *argv[])
{int fd;char *addr = NULL;fd = open("/dev/remap_pfn", O_RDWR);if (fd < 0) {perror("open failed\n");exit(-1);}addr = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, OFFSET);if (!addr) {perror("mmap failed\n");exit(-1);}for (int i = 0; i < 16; i++) {for (int j = 0; j < 10; j++) {if (*(uint8_t *)((uintptr_t)addr + i*PAGE_SIZE + j) != j) {printf("error, i: %d, j: %d", i, j);} else {printf("i: %d, j: %d, data: 0x%x\n", i, j, *(uint8_t *)((uintptr_t)addr + i*PAGE_SIZE + j));}}}return 0;
}
测试结果:
用户进程2的打印:
相关文章:

在Linux中将设备驱动的地址映射到用户空间
本期主题: MMU的简单介绍,以及如何实现设备地址映射到用户空间 往期链接: Linux内核链表零长度数组的使用inline的作用嵌入式C基础——ARRAY_SIZE使用以及踩坑分析Linux下如何操作寄存器(用户空间、内核空间方法讲解)…...

电脑自带dll修复在哪里,dll丢失的6种解决方法总结
在现代科技日新月异的时代,电脑已经成为我们生活中不可或缺的一部分。然而,在使用电脑的过程中,我们常常会遇到一些常见的问题,其中之一就是dll文件丢失或损坏。当这些dll文件丢失或损坏时,可能会导致某些应用程序无法…...
k8s基于nfs创建storageClass
首先安装nfs #服务端安装 yum install -y nfs-utils rpcbind #客户端安装 yum install -y nfs-utils #启动服务 并设置开启启动 systemctl start rpcbind && systemctl enable rpcbind systemctl start nfs && systemctl enable nfs #创建共享目录 mkdir -p /…...

Chrome无法拖入加载.crx扩展文件(以IDM为例)
问题原因:新版本的Chrome浏览器已不支持加载.crx文件 解决办法:将.crx文件压缩为.zip文件,解压缩后再加载到Chrome中 以IDM的.crx文件作为示例; IDM的.crx文件位于C:\Program Files (x86)\Internet Download Manager; 将IDMGCE…...

数字教学时代:构建高效在线帮助中心的重要性
在数字化教学日益普及的今天,教育领域正经历着前所未有的变革。随着在线课程、虚拟教室、智能学习平台等数字化工具的广泛应用,教育资源的获取方式和学习模式发生了深刻变化。然而,这种变革也带来了新的挑战,其中之一便是如何确保…...

828华为云征文|华为云弹性云服务器FlexusX实例下的Nginx性能测试
本文写的是华为云弹性云服务器FlexusX实例下的Nginx性能测试 目录 一、华为云弹性云服务器FlexusX实例简介二、测试环境三、测试工具四、测试方法五、测试结果 下面是华为云弹性云服务器FlexusX实例下的Nginx性能测试。 一、华为云弹性云服务器FlexusX实例简介 华为云弹性云服…...
知识图谱入门——2:技术体系基本概念:知识表示与建模、知识抽取与挖掘、知识存储与融合、知识推理与检索
知识图谱是通过构建“实体”和“关系”来描述世界的信息网络,它不仅是数据的存储方式,还可以支持推理与查询,帮助系统更好地理解、整合和利用数据。 文章目录 1. 知识表示与建模2. 知识抽取与挖掘3. 知识存储与融合4. 知识推理与检索总结 1.…...

【不看会后悔系列】排序之——文件归并【史上最全详解】~
文章目录 前言一、何为文件归并?二、文件归并思路分析三、创造多数据文件四、前置准备——堆排序五、两个文件写入到第三个文件六、读 N 个数据返回给文件,并返回读到数据的个数七、文件归并八、文件归并完整代码总结1. 运行代码2. 运行截图 总结 前言 学习了归并排…...
安全点的应用场景及其原理详解
引言 在Java虚拟机(JVM)运行的过程中,有些时刻,系统需要暂停所有正在运行的线程,以执行某些全局操作或确保数据的一致性。这些暂停线程的时刻被称为**“安全点”**(Safepoint)。尽管安全点最广…...
计算机各专业2025毕业设计选题推荐【各专业 | 最新】
计算机各专业2025毕业设计选题推荐 Java、Python、Vue、PHP、小程序、安卓、大数据、爬虫、可视化、机器学习、深度学习 文末有联系方式~~~ 1.Java 基于Java的在线购物系统设计与实现Java开发的图书管理系统基于Spring Boot的社交媒体平台Java实现的移动健康应用在线学习平…...

【Python报错已解决】IndexError: index 0 is out of bounds for axis 1 with size 0
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 专栏介绍 在软件开发和日常使用中,BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…...

SpringGateway(网关)微服务
一.启动nacos 1.查看linux的nacos是否启动 docker ps2.查看是否安装了nacos 前面是你的版本,后面的names是你自己的,我们下面要启动的就是这里的名字。 docker ps -a3.启动nacos并查看是否启动成功 二.创建网关项目 1.创建idea的maven项目 2.向pom.x…...
jQuery面试题:(第三天)
8.你在jQuery中使用过哪些插入节点的方法,它们的区别是什么? 答:append(),appendTo(),prepend(),prependTo(),after(),insertAfter() before(),insertBefore() 内添加 1.append()在文档内添加元素 2.appendTo()把匹配的元素添加到对象里 3.prepend()…...

聊聊国内首台重大技术装备(2)
上次,介绍了《首台(套)重大技术装备推广应用指导目录(2024年版)》中介绍的硅外延炉,湿法清洗机,氧化炉,见文章: 《聊聊国内首台重大技术装备(1)》…...
python 实现rayleigh quotient瑞利商算法
rayleigh quotient瑞利商算法介绍 瑞利商(Rayleigh Quotient)算法在多个领域,如线性代数、计算机视觉和机器学习等,都有重要的应用。瑞利商定义为函数 R ( A , x ) ( x H A x ) / ( x H x ) R(A, x) (x^H Ax) / (x^H x) R(A,x)…...

Java Web应用升级故障案例解析
在一次Java Web应用程序的优化升级过程中,从Tomcat 7.0.109版本升级至8.5.93版本后,尽管在预发布环境中验证无误,但在灰度环境中却发现了一个令人困惑的问题:新日志记录神秘“失踪”。本文深入探讨了这一问题的排查与解决过程&…...

Java类和对象、自定义包、static、代码块、方法重写
目录 1.类和对象 2.this指针 3.对象的构造和初始化 3.1默认初始化 3.2就地初始化 3.3构造初始化 3.4IDEA快速填充 3.5使用this简化 3.6初始化的总结 4.包的引入 4.1包的概念 4.2导入包中的类 4.3自定义包 5.static修饰 6.代码块的划分 7.方法重写 1.类和对象 使…...

【系统代码】招投标采购一体化管理系统,JAVA+vue
前言: 随着互联网和数字技术的不断发展,企业采购管理逐渐走向数字化和智能化。数字化采购平台作为企业采购管理的新模式,能够提高采购效率、降低采购成本、优化供应商合作效率,已成为企业实现效益提升的关键手段。系统获取在文末…...

基于yolov8深度学习的120种犬类检测与识别系统python源码+onnx模型+评估指标曲线+精美GUI界面目标检测狗类检测犬类识别系统
【算法介绍】 基于YOLOv8深度学习的120种犬类检测与识别系统是一款功能强大的工具,该系统利用YOLOv8深度学习框架,通过21583张图片的训练,实现了对120种犬类的精准检测与识别。 该系统基于Python与PyQt5开发,具有简洁的UI界面&a…...
UNI-APP_iOS开发技巧之:跳转到TestFlight或者App Store
有的时候我们的应用可能需要上TestFlight或者App Store,更新升级就需要跳到TestFlight里面。方法如下: 跳转到TestFlight: itms-beta://itunes.apple.com/app/你的AppID 跳转到AppStore: itms-apps://itunes.apple.com/app/你的AppIDhttps://airp…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...