当前位置: 首页 > news >正文

在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. 存在的原因

首先理解一下,为什么需要有虚拟内存这么一个概念,总结了以下好处:

  1. 进程隔离和内存保护,如果没有虚拟内存,假如A进程发生了地址访问问题,可能直接把不相关的B进程也影响到了,这样是不合理的。虚拟内存的方法就给每个进程提供了自己的虚拟空间,增加了系统的安全性和稳定性。
  2. 地址空间重用,物理内存地址是有限且连续的,而虚拟内存允许操作系统在物理内存中灵活地管理进程使用的地址空间。进程的虚拟地址空间可以是连续的,而实际的物理内存可以是不连续的,这种方式更高效地使用内存并减少碎片。
  3. 简化编程模型,使用虚拟内存,开发人员可以编写和管理程序而不必担心物理内存的实际大小和地址布局,这让开发变得更简单。

2. 内存管理单元

高性能处理器一般会提供一个内存管理单元MMU,该单元辅助操作系统进行内存管理,现代处理器常用的方案是 虚拟寻址方式(virtual addressing),参考下图:
在这里插入图片描述
简单介绍:

使用虚拟寻址时

  1. CPU会通过虚拟地址(virtual address, VA)来访问主存
  2. MMU进行地址翻译,把虚拟地址转换成物理地址
  3. 最后进行访问

2. 将设备地址映射到用户空间

1. 原理说明

一般情况下,用户空间是不能直接访问设备的寄存器或者地址空间的
但是,可以在设备驱动程序中实现mmap()函数,这个函数可以使得用户空间直接访问设备的地址空间。

mmap实现了这样的映射过程:

  1. 将用户空间的一段内存与设备内存关联
  2. 当用户访问用户空间的这段地址范围时,转换成对设备的访问

这种特性对显示一类的设备非常有意义,这样在用户空间就可以直接访问了,不需要从内核空间到用户空间的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例子

例子流程:

  1. 驱动里分配好空间
  2. 分配的空间,32PAGE,128KB,虚拟地址通过驱动的mmap file_operations映射出去
  3. 用户进程通过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中将设备驱动的地址映射到用户空间

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

电脑自带dll修复在哪里,dll丢失的6种解决方法总结

在现代科技日新月异的时代&#xff0c;电脑已经成为我们生活中不可或缺的一部分。然而&#xff0c;在使用电脑的过程中&#xff0c;我们常常会遇到一些常见的问题&#xff0c;其中之一就是dll文件丢失或损坏。当这些dll文件丢失或损坏时&#xff0c;可能会导致某些应用程序无法…...

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为例)

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

数字教学时代:构建高效在线帮助中心的重要性

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

828华为云征文|华为云弹性云服务器FlexusX实例下的Nginx性能测试

本文写的是华为云弹性云服务器FlexusX实例下的Nginx性能测试 目录 一、华为云弹性云服务器FlexusX实例简介二、测试环境三、测试工具四、测试方法五、测试结果 下面是华为云弹性云服务器FlexusX实例下的Nginx性能测试。 一、华为云弹性云服务器FlexusX实例简介 华为云弹性云服…...

知识图谱入门——2:技术体系基本概念:知识表示与建模、知识抽取与挖掘、知识存储与融合、知识推理与检索

知识图谱是通过构建“实体”和“关系”来描述世界的信息网络&#xff0c;它不仅是数据的存储方式&#xff0c;还可以支持推理与查询&#xff0c;帮助系统更好地理解、整合和利用数据。 文章目录 1. 知识表示与建模2. 知识抽取与挖掘3. 知识存储与融合4. 知识推理与检索总结 1.…...

【不看会后悔系列】排序之——文件归并【史上最全详解】~

文章目录 前言一、何为文件归并&#xff1f;二、文件归并思路分析三、创造多数据文件四、前置准备——堆排序五、两个文件写入到第三个文件六、读 N 个数据返回给文件,并返回读到数据的个数七、文件归并八、文件归并完整代码总结1. 运行代码2. 运行截图 总结 前言 学习了归并排…...

安全点的应用场景及其原理详解

引言 在Java虚拟机&#xff08;JVM&#xff09;运行的过程中&#xff0c;有些时刻&#xff0c;系统需要暂停所有正在运行的线程&#xff0c;以执行某些全局操作或确保数据的一致性。这些暂停线程的时刻被称为**“安全点”**&#xff08;Safepoint&#xff09;。尽管安全点最广…...

计算机各专业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

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…...

SpringGateway(网关)微服务

一.启动nacos 1.查看linux的nacos是否启动 docker ps2.查看是否安装了nacos 前面是你的版本&#xff0c;后面的names是你自己的&#xff0c;我们下面要启动的就是这里的名字。 docker ps -a3.启动nacos并查看是否启动成功 二.创建网关项目 1.创建idea的maven项目 2.向pom.x…...

jQuery面试题:(第三天)

8.你在jQuery中使用过哪些插入节点的方法&#xff0c;它们的区别是什么&#xff1f; 答:append(),appendTo(),prepend(),prependTo(),after(),insertAfter() before(),insertBefore() 内添加 1.append()在文档内添加元素 2.appendTo()把匹配的元素添加到对象里 3.prepend()…...

聊聊国内首台重大技术装备(2)

上次&#xff0c;介绍了《首台&#xff08;套&#xff09;重大技术装备推广应用指导目录&#xff08;2024年版&#xff09;》中介绍的硅外延炉&#xff0c;湿法清洗机&#xff0c;氧化炉&#xff0c;见文章&#xff1a; 《聊聊国内首台重大技术装备&#xff08;1&#xff09;》…...

python 实现rayleigh quotient瑞利商算法

rayleigh quotient瑞利商算法介绍 瑞利商&#xff08;Rayleigh Quotient&#xff09;算法在多个领域&#xff0c;如线性代数、计算机视觉和机器学习等&#xff0c;都有重要的应用。瑞利商定义为函数 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应用程序的优化升级过程中&#xff0c;从Tomcat 7.0.109版本升级至8.5.93版本后&#xff0c;尽管在预发布环境中验证无误&#xff0c;但在灰度环境中却发现了一个令人困惑的问题&#xff1a;新日志记录神秘“失踪”。本文深入探讨了这一问题的排查与解决过程&…...

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

前言&#xff1a; 随着互联网和数字技术的不断发展&#xff0c;企业采购管理逐渐走向数字化和智能化。数字化采购平台作为企业采购管理的新模式&#xff0c;能够提高采购效率、降低采购成本、优化供应商合作效率&#xff0c;已成为企业实现效益提升的关键手段。系统获取在文末…...

基于yolov8深度学习的120种犬类检测与识别系统python源码+onnx模型+评估指标曲线+精美GUI界面目标检测狗类检测犬类识别系统

【算法介绍】 基于YOLOv8深度学习的120种犬类检测与识别系统是一款功能强大的工具&#xff0c;该系统利用YOLOv8深度学习框架&#xff0c;通过21583张图片的训练&#xff0c;实现了对120种犬类的精准检测与识别。 该系统基于Python与PyQt5开发&#xff0c;具有简洁的UI界面&a…...

UNI-APP_iOS开发技巧之:跳转到TestFlight或者App Store

有的时候我们的应用可能需要上TestFlight或者App Store&#xff0c;更新升级就需要跳到TestFlight里面。方法如下&#xff1a; 跳转到TestFlight: itms-beta://itunes.apple.com/app/你的AppID 跳转到AppStore: itms-apps://itunes.apple.com/app/你的AppIDhttps://airp…...

基于SSM+Vue技术的定制式音乐资讯平台

文未可获取一份本项目的java源码和数据库参考。 一、选题的背景与意义&#xff1a; 随着个人计算机的普及和互联网技术的日渐成熟&#xff0c;网络正逐渐成为人们获取信息及消费的主要渠道。然而在当前这个信息时代&#xff0c;网络中的信息种类和数量呈现爆炸性增长的趋势&a…...

Spring依赖注入和注解驱动详解和案例示范

在 Spring 框架中&#xff0c;依赖注入&#xff08;Dependency Injection, DI&#xff09;和注解驱动&#xff08;Annotation-Driven&#xff09;是其核心机制&#xff0c;它们为 Spring 应用提供了灵活性和可扩展性。依赖注入简化了对象间的依赖管理&#xff0c;而注解驱动则通…...

网络通信——OSPF协议(基础篇)

这里基础是因为没有讲解OSPF中的具体算法过程&#xff0c;以及其中很多小细节。后续会更新。 目录 一.OSPF的基础信息 二.认识OSPF中的Router ID 三.OSPF中的三张表 四.OSPF中的度量方法&#xff08;计算开销值&#xff09; 五. OSPF选举DR和BDR&#xff08;就是这个区域…...

Kubernetes从零到精通(15-安全)

目录 一、Kubernetes API访问控制 1.传输安全(Transport Security) 2.认证(Authentication) 2.1 认证方式 2.2 ServiceAccount和普通用户的区别 2.3 ServiceAccount管理方式 自动ServiceAccount示例 手动ServiceAccount示例 3.鉴权 (Authorization) 3.1鉴权方式 3.2 …...

《蓝桥杯算法入门》(C/C++、Java、Python三个版本)24年10月出版

推荐&#xff1a;《算法竞赛》&#xff0c;算法竞赛大全书&#xff0c;网购&#xff1a;京东 天猫  当当 文章目录 《蓝桥杯算法入门》内容简介本书读者对象作者简介联系与交流《蓝桥杯算法入门 C/C》版目录 《蓝桥杯算法入门 Java》版目录 《蓝桥杯算法入门 Python》版目录 …...

Soar项目中添加一条新的SQL审核规则示例

soar是一个开源的SQL规则审核工具&#xff0c;是一个go语言项目&#xff0c;可以直接编译构建成一个可执行程序&#xff0c;而且是一个命令行工具&#xff0c;我们可以利用archey来调用soar进行sql规则审核以及sql的分析&#xff0c;包括执行计划的查看及sql建议等。 soar中已…...

RISC-V开发 linux下GCC编译自定义指令流程笔记

第一步&#xff1a;利用GCC提供了内嵌汇编的功能可以在C代码中直接内嵌汇编语言 第二步&#xff1a;利用RSIC-V的中的.insn模板进行自定义指令的插入 第三步&#xff1a;RISC-V开发环境的搭建 C语言插入汇编 GCC提供了内嵌汇编的功能可以在C代码中直接内嵌汇编语言语句方便了…...

java代码是如何与数据库通信的?

Java代码与数据库通信的过程主要通过Java Database Connectivity&#xff08;JDBC&#xff09;来实现。JDBC是Java与数据库之间的标准接口&#xff0c;提供了用于执行SQL语句和处理数据库结果的API。以下是Java代码与数据库通信的详细步骤&#xff1a; 一、导入JDBC库 在Java…...

gateway--网关

在微服务架构中&#xff0c;Gateway&#xff08;网关&#xff09;是一个至关重要的组件&#xff0c;它扮演着多种关键角色&#xff0c;包括路由、负载均衡、安全控制、监控和日志记录等。 Gateway网关的作用 统一访问入口&#xff1a; Gateway作为微服务的统一入口&#xff0c…...

北京数字孪生工业互联网可视化技术,赋能新型工业化智能制造工厂

随着北京数字孪生工业互联网可视化技术的深入应用&#xff0c;新型工业化智能制造工厂正逐步迈向智能化、高效化的全新阶段。这项技术不仅实现了物理工厂与数字世界的精准映射&#xff0c;更通过大数据分析、人工智能算法等先进手段&#xff0c;为生产流程优化、资源配置合理化…...

手机wordpress无法登录/百度提交收录入口

2019独角兽企业重金招聘Python工程师标准>>> 1.Redis简介 Redis 是一个开源&#xff08;BSD许可&#xff09;的&#xff0c;内存中的key-value数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息中间件&#xff0c;解决了断电后数据完全丢失的情况。它支持多…...

用asp做网站流程/重庆森林台词

今年的面试主要是技术面试 1、项目是怎么做的 常规问题 --略 2、判断项目的可以上线的标准是什么&#xff1f; ①与需求一致&#xff0c;符合客户的要求 ②用例执行完成 ③bug和研发、产品这边确定必须改的改完 ④进行release版本回归无bug 3、给你一个登陆功能&#xff0…...

自己做网站用买域名吗/吉林网络推广公司

每天利用计划任务在凌晨1点自动执行&#xff0c;备份zabbix的数据库至本地的/backup/mysql_backup目录 #!/bin/sh DUMP/usr/bin/mysqldump OUT_DIR/backup/mysql_backup LINUX_USERroot DB_NAMEzabbix DB_USERroot DB_PASS123456 cd $OUT_DIR DATEdate %Y%m%d OUT_SQL"$DA…...

免费建站赚钱/今日十大热点新闻

作者&#xff1a;张伟 爱可生北京分公司 DBA 团队成员&#xff0c;负责 MySQL 日常问题处理和 DMP 产品维护。喜爱技术和开源数据库&#xff0c;喜爱运动、读书、电影&#xff0c;花草树木。 本文来源&#xff1a;原创投稿 *爱可生开源社区出品&#xff0c;原创内容未经授权不得…...

做网站需要前置审批/百度快速排名软件下载

chatGPT基于所谓的大模型&#xff0c;这里有两个关键词一个是“大”&#xff0c;一个是“模型”&#xff0c;我们先看什么叫“模型”。所谓模型其实就是深度学习中的神经网络&#xff0c;后者由很多个称之为“神经元”基本单元组成。神经元是一种基础计算单元&#xff0c;它执行…...

php做网站怎么布局/网络违法犯罪举报网站

Python中的字典特点&#xff1a; 速度快&#xff0c;内部使用二分查找的方式 可以用来存储大量的关系型数据 字典是无序的 字典的定义方式&#xff1a; dic dict(name ”zhangsan”, age 19) dic1 dict(((“name”, “zhangsan”), (“age”, 19))) dic2 {“name”:”la…...