Linux系统 mmap 存储映射

2024-04-26 03:08
文章标签 linux 系统 存储 映射 mmap

本文主要是介绍Linux系统 mmap 存储映射,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

设备缓存与设备内存

设备缓存位于主内存中,是驱动程序管理的一段内存区域,比如framebuffer, driver工作需要的scratch pad buffer等等。

而设备内存是位于设备上的,和设备是分不开的,比如某些设备上的FIFO,SRAM,或者显卡上的DOORBELL区等,都属于设备内存。

不同的内存映射给用户态或者内核态的时候,需要使用不同的API。驱动开发者需要保证在正确的状态下使用正确的接口将虚拟地址映射到正确的存储类型上,不能混用。

mmap函数原型: 

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
addr:推荐映射定义,如果为NULL,内核自动分配,如果flags设置了MAP_FIXED且addr合法,则映射addr开始的地址并返回。
length: length是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起,一般为PAGE整数倍,不足一内存页按一内存页处理.
prot:映射读写属性等等,期望的内存保护标志,不能与文件的打开模式冲突.
flags:私有,共享,匿名等等MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。MAP_FIXED:映射addr开始的地址,如果事先存在VMA,则删除原来的在做映射
fd:对于file backend映射,需要指定文件fd,如果为匿名映射,则为-1.
offset:文件内字节从偏移处开始映射

根据内核代码,offset参数的单位是字节,内核在调用ksys_mmap_pgoff前会将其单位转换为page。内核中mmap函数的vm_pgoff来源于off >> PAGE_SHIFT,在mmap实现中可以根据此映射fd文件的不同区域。

映射属性:

映射建立后,会在进程的地址空间中创建一个struct vm_area_struct对象,来描述虚拟地址空间的信息,信息包括起始地址,区域大小,映射属性FLAG等等,FLAG包括:

VM_IO: Memory Maped I/O or similary.(MMIO).

VM_DONTCOPY: 在fork子进程的时候不要拷贝这个部分。

VM_DONTEXPAND: 不能用mremap 函数扩展映射区域。

VM_DONTDUMP:在coredump时不保存这个部分。

VM_PFNMAP: 这个比较重要,它表示这个映射针对的是PURE PFN,纯物理空间,没有struct page 对其进行管理。所以有些情况下,使用受到限制,通常设备的IO映射以及PCI BAR空间的映射,都会带有这个标志。比如AMD KFD GPU驱动中在IO映射时就设置了这个标志:

/dev/mem设备节点可以将物理内存全部映射到用户态,这里实践一把。

配置:

为了启用/dev/mem设备节点,并且BYPASS调如下的检查逻辑,需要设置内核如下的配置单,CONFIG_STRICT_DEVMEM主要是对映射范围进行安全性检查,避免将一些限制区域映射到用户态。

CONFIG_DEVMEM=y
# CONFIG_STRICT_DEVMEM is not set
# CONFIG_X86_PAT is not set

来自于LDD3的测试代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <limits.h>int main(int argc, char **argv)
{char *fname;FILE *f;unsigned long offset, len;void *address;if (argc !=4|| sscanf(argv[2],"%li", &offset) != 1|| sscanf(argv[3],"%li", &len) != 1) {fprintf(stderr, "%s: Usage \"%s <file> <offset> <len>\"\n", argv[0],argv[0]);exit(1);}/* the offset might be big (e.g., PCI devices), but conversion trims it */if (offset == INT_MAX) {if (argv[2][1]=='x')sscanf(argv[2]+2, "%lx", &offset);elsesscanf(argv[2], "%lu", &offset);}fname=argv[1];if (!(f=fopen(fname,"r"))) {fprintf(stderr, "%s: %s: %s\n", argv[0], fname, strerror(errno));exit(1);}address=mmap(0, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fileno(f), offset);if (address == (void *)-1) {fprintf(stderr,"%s: mmap(): %s\n",argv[0],strerror(errno));exit(1);}fclose(f);fprintf(stderr, "mapped \"%s\" from %lu (0x%08lx) to %lu (0x%08lx)\n",fname, offset, offset, offset+len, offset+len);fwrite(address, 1, len, stdout);return 0;
}
$ sudo ./a.out /dev/mem  0x10000 0x1000|od -Ax -t x1

这段程序非常好用,基本上可以映射/dev/mem背后的所有物理内存,上面的例子不够特殊,我们以物理地址0内存为例,首先看到物理内存确实是从0开始,0物理地址映射的确实是RAM,通过内核模块获取page_address(pfn_to_page(0))地址,打印其中内容:

然后xxd直接获取设备内容如下:

ldd程序获取内容如下:

可见三种方法得到的数据完全一致。

devmem2

开源工具devmem2是一个LINUX命令行工具,用于读写物理内存地址,通过将/dev/mem映射到进程地址空间,devmem2允许用户直接访问物理内存。项目源码在如下地址:

GitHub - radii/devmem2: devmem2 - simple program to read/write from/to any location in memory.

CONFIG_X86_PAT

根据menuconfig的解释,PAT是页面属性表的简称(Page Attribute Table),功能是传统的MTRR的(Memory Type Range Register)扩展和增强,目的是对页面的CACHE访问属性进行配置,比如可用于为不同的用途(例如,可缓存,不可更改,写合并等)指定物理地址空间的不同部分。

PAT(Page Attribute Table) 是在线性地址空间中对映射的物理内存的一种缓存策略的描述,物理内存中的一个物理页框采用什么样的缓存策略,是由两方面决定的,一个是在物理地址空间进行描述的MTRR,一个是线性空间的PAT。

Write Combine 和 Cache 是什么关系 - 知乎

配置CONFIG_X86_PAT的目的是DISABLE如下的检查。

remap_pfn_range->track_pfn_remap(vma, &prot, remap_pfn, addr, PAGE_ALIGN(size));

如果映射区有设置CACHE 属性,则调用lookup_memtyp寻找:

设置文件在arch/x86/mm/pat.c这里。

PCIE 空间的配置方式:

PCIe架构定义了4种地址空间:配置空间、Memory空间、IO空间和message空间:

每个PCIe Function都有4KB的配置空间(Configuration Space)。前256 Bytes 是和PCI 兼容的配置空间(64B是HEADER),剩余的是PCIe扩展配置空间(Extended Configuration Space)。

PCIe设备声明的两种不同类型的MMIO:可预取MMIO(Prefetchable MMIO,P-MMIO)和不可预取MMIO(Non-Prefetchable MMIO,NP-MMIO)。可预取空间有两个意义十分明确的属性:

  • 读操作不存在副作用。(Reads do not have side effects)(读操作不会出发设备逻辑操作)
  • 允许写合并(Write merging is allowed)(Write Conbined.)

X86采用独立编址的方式,将memory操作与外设IO操作分开了,才有了memory空间和IO空间的区分。ARM采用统一编码的方式。

登记物理内存的CACHE属性

包括系统内存和设备内存在内,按照归属分成不同的区段,内核驱动模块可以调用reserve_memtype,传入物理地址和SIZE大小,将期望的CACH属性传递进去,函数会维护建立i一个红黑树,维护所有这些区段的CACHE属性,当驱动进行实际的映射时,会调用track_pfn_remap->lookup_memtype寻找对应的属性,将其设置在MMU的PTE页表中生效,运行机制如此。

调试PAT属性

debugfs中存在pat_memtype_list文件节点,可以调试系统中记录的物理内存区的CACHE属性:,使用如下方式查看

cat /sys/kernel/debug/x86/pat_memtype_list

可以看到,显存的CACHE属性为WC,表示可以将零散数据BUST起来,增加传输效率。

以我的集成显卡为例,BAR地址是0xb0000000,属性也是WC:

这篇关于Linux系统 mmap 存储映射的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/936549

相关文章

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

Linux安装MySQL的教程

《Linux安装MySQL的教程》:本文主要介绍Linux安装MySQL的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux安装mysql1.Mysql官网2.我的存放路径3.解压mysql文件到当前目录4.重命名一下5.创建mysql用户组和用户并修