用户态内存映射

简介: 【9月更文挑战第20天】内存映射不仅包括物理与虚拟内存间的映射,还涉及将文件内容映射至虚拟内存,使得访问内存即可获取文件数据。mmap 系统调用支持将文件或匿名内存映射到进程的虚拟内存空间,通过多级页表机制实现高效地址转换,并利用 TLB 加速映射过程。TLB 作为页表缓存,存储频繁访问的页表项,显著提升了地址转换速度。

内存映射不仅仅是物理内存和虚拟内存之间的映射,还包括将文件中的内容映射到虚拟内存空间。这个时候,访问内存空间就能够访问到文件里面的数据。而仅有物理内存和虚拟内存的映射,是一种特殊情况。

对于堆的申请来讲,mmap 是映射内存空间到物理内存。

如果一个进程想映射一个文件到自己的虚拟内存空间,也要通过 mmap 系统调用。这个时候 mmap 是映射内存空间到物理内存再到文件。

如果是匿名映射,则调用 mm_struct 里面的 get_unmapped_area 函数。这个函数其实是 arch_get_unmapped_area。它会调用 find_vma_prev,在表示虚拟内存区域的 vm_area_struct 红黑树上找到相应的位置。之所以叫 prev,是说这个时候虚拟内存区域还没有建立,找到前一个 vm_area_struct。

如果不是匿名映射,而是映射到一个文件,这样在 Linux 里面,每个打开的文件都有一个 struct file 结构,里面有一个 file_operations,用来表示和这个文件相关的操作。如果是我们熟知的 ext4 文件系统,调用的是 thp_get_unmapped_area。如果我们仔细看这个函数,最终还是调用 mm_struct 里面的 get_unmapped_area 函数。殊途同归。

PGD、P4G、PUD、PMD、PTE四级页表的概念如下:

pgd_t 用于全局页目录项,pud_t 用于上层页目录项,pmd_t 用于中间页目录项,pte_t 用于直接页表项。

一个进程的虚拟地址空间包含用户态和内核态两部分。为了从虚拟地址空间映射到物理页面,页表也分为用户地址空间的页表和内核页表,这就和上面遇到的 vmalloc 有关系了。在内核里面,映射靠内核页表,这里内核页表会拷贝一份到进程的页表。

cr3 是 CPU 的一个寄存器,它会指向当前进程的顶级 pgd。如果 CPU 的指令要访问进程的虚拟内存,它就会自动从 cr3 里面得到 pgd 在物理内存的地址,然后根据里面的页表解析虚拟内存的地址为物理内存,从而访问真正的物理内存上的数据。

这里需要注意两点。第一点,cr3 里面存放当前进程的顶级 pgd,这个是硬件的要求。cr3 里面需要存放 pgd 在物理内存的地址,不能是虚拟地址。因而 load_new_mm_cr3 里面会使用 __pa,将 mm_struct 里面的成员变量 pgd(mm_struct 里面存的都是虚拟地址)变为物理地址,才能加载到 cr3 里面去。

第二点,用户进程在运行的过程中,访问虚拟内存中的数据,会被 cr3 里面指向的页表转换为物理地址后,才在物理内存中访问数据,这个过程都是在用户态运行的,地址转换的过程无需进入内核态。

只有访问虚拟内存的时候,发现没有映射到物理内存,页表也没有创建过,才触发缺页异常。进入内核调用 do_page_fault,一直调用到 __handle_mm_fault,这才有了上面解析到这个函数的时候,我们看到的代码。既然原来没有创建过页表,那只好补上这一课。于是,__handle_mm_fault 调用 pud_alloc 和 pmd_alloc,来创建相应的页目录项,最后调用 handle_pte_fault 来创建页表项。

为了加快映射速度,我们不需要每次从虚拟地址到物理地址的转换都走一遍页表。

页表一般都很大,只能存放在内存中。操作系统每次访问内存都要折腾两步,先通过查询页表得到物理地址,然后访问该物理地址读取指令、数据。

为了提高映射速度,我们引入了 TLB(Translation Lookaside Buffer),我们经常称为快表,专门用来做地址映射的硬件设备。它不在内存中,可存储的数据比较少,但是比内存要快。所以,我们可以想象,TLB 就是页表的 Cache,其中存储了当前最可能被访问到的页表项,其内容是部分页表项的一个副本。

有了 TLB 之后,地址映射的过程就像图中画的。我们先查块表,块表中有映射关系,然后直接转换为物理地址。如果在 TLB 查不到映射关系时,才会到内存中查询页表。



相关文章
|
27天前
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
37 0
|
2月前
|
存储 安全 Linux
将文件映射到内存,像数组一样访问
将文件映射到内存,像数组一样访问
27 0
|
2月前
|
消息中间件 Linux 容器
共享内存的创建和映射过程
【9月更文挑战第1天】消息队列、共享内存及信号量在使用前需生成key并获取唯一ID,均通过`xxxget`函数实现。
|
5月前
|
监控 Linux
深入了解Linux的pmap命令:进程内存映射的利器
`pmap`是Linux下分析进程内存映射的工具,显示内存区域、权限、大小等信息。通过`/proc/[pid]/maps`获取数据,特点包括详细、实时和灵活。参数如`-x`显示扩展信息,`-d`显示设备。示例:`pmap -x 1234`查看进程1234的映射。注意权限、实时性和准确性。结合其他工具定期监控,排查内存问题。
|
6月前
内存映射mmap拓展
内存映射mmap拓展
|
6月前
内存映射实现无血缘关系进程间通信
内存映射实现无血缘关系进程间通信
|
6月前
内存映射实现父子进程通信
内存映射实现父子进程通信
|
6月前
|
存储 算法 内存技术
深入理解操作系统内存管理:从虚拟内存到物理内存的映射
【4月更文挑战第30天】 在现代操作系统中,内存管理是一个复杂而关键的功能。它不仅确保了系统资源的有效利用,还为每个运行的程序提供了独立的地址空间,保障了程序之间的隔离性和安全性。本文将探讨操作系统如何通过分页机制和虚拟内存技术实现内存的抽象化,以及这些技术是如何影响应用程序性能的。我们将详细解析虚拟地址到物理地址的转换过程,并讨论操作系统在此过程中扮演的角色。文章的目的是为读者提供一个清晰的框架,以便更好地理解内存管理的工作原理及其对系统稳定性和效率的影响。
|
6月前
|
算法 安全 Linux
Linux 下共享内存方式 :System V共享内存、共享文件映射(mmap)、POSIX共享内存对比...
Linux 下共享内存方式 :System V共享内存、共享文件映射(mmap)、POSIX共享内存对比...
125 2
|
6月前
|
存储 大数据 Python
NumPy中的内存映射文件处理技巧
【4月更文挑战第17天】NumPy的`memmap`模块用于处理大数据,通过内存映射文件技术实现对磁盘文件的高效访问,无需一次性加载到内存。创建内存映射数组使用`numpy.memmap`,并可像操作普通数组一样读写。最佳实践包括选择合适数据类型、规划文件大小和形状、减少磁盘操作、确保文件安全性和一致性及管理内存使用。内存映射是处理超出内存数据集的有效策略。