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

相关文章

电脑找不到mfc90u.dll文件怎么办? 系统报错mfc90u.dll丢失修复的5种方案

《电脑找不到mfc90u.dll文件怎么办?系统报错mfc90u.dll丢失修复的5种方案》在我们日常使用电脑的过程中,可能会遇到一些软件或系统错误,其中之一就是mfc90u.dll丢失,那么,mf... 在大部分情况下出现我们运行或安装软件,游戏出现提示丢失某些DLL文件或OCX文件的原因可能是原始安装包

电脑显示mfc100u.dll丢失怎么办?系统报错mfc90u.dll丢失5种修复方案

《电脑显示mfc100u.dll丢失怎么办?系统报错mfc90u.dll丢失5种修复方案》最近有不少兄弟反映,电脑突然弹出“mfc100u.dll已加载,但找不到入口点”的错误提示,导致一些程序无法正... 在计算机使用过程中,我们经常会遇到一些错误提示,其中最常见的就是“找不到指定的模块”或“缺少某个DL

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

Linux之systemV共享内存方式

《Linux之systemV共享内存方式》:本文主要介绍Linux之systemV共享内存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、工作原理二、系统调用接口1、申请共享内存(一)key的获取(二)共享内存的申请2、将共享内存段连接到进程地址空间3、将

快速修复一个Panic的Linux内核的技巧

《快速修复一个Panic的Linux内核的技巧》Linux系统中运行了不当的mkinitcpio操作导致内核文件不能正常工作,重启的时候,内核启动中止于Panic状态,该怎么解决这个问题呢?下面我们就... 感谢China编程(www.chinasem.cn)网友 鸢一雨音 的投稿写这篇文章是有原因的。为了配置完

关于MongoDB图片URL存储异常问题以及解决

《关于MongoDB图片URL存储异常问题以及解决》:本文主要介绍关于MongoDB图片URL存储异常问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录MongoDB图片URL存储异常问题项目场景问题描述原因分析解决方案预防措施js总结MongoDB图

Linux命令之firewalld的用法

《Linux命令之firewalld的用法》:本文主要介绍Linux命令之firewalld的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux命令之firewalld1、程序包2、启动firewalld3、配置文件4、firewalld规则定义的九大

Linux之计划任务和调度命令at/cron详解

《Linux之计划任务和调度命令at/cron详解》:本文主要介绍Linux之计划任务和调度命令at/cron的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux计划任务和调度命令at/cron一、计划任务二、命令{at}介绍三、命令语法及功能 :at

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效