你真的了解mmap映射类型​嘛?

mmap的映射类型你真的了解吗?下面将进行详细的介绍


mmap 根据映射的类型,有四种最常用的组合:​

私有匿名映射,用于分配堆空间;​

共享匿名映射,用于父子进程之间通讯;​

私有文件映射,用于加载动态链接库;​

共享文件映射,用于多进程之间通讯。

私有匿名映射


        私有匿名映射是最简单的情况。调用 mmap 时,会在文件映射区域分配一块内存,并创建对应的 vma 结构,此次调用完成后,内核不对这块虚拟内存进行物理内存映射。当访问这块虚拟内存时,会触发缺页中断,但与 execve 时的缺页中断不同,这是因为这次是匿名映射,没有关联文件属性。内核会调用 doanonymouspage 分配一个物理内存页,并将其初始化为 0,然后在页表中建立虚拟地址到物理地址的映射关系。


私有文件映射


        在内核中,每个进程打开文件时,其 PCB 中会包含一个 struct file 结构。如果进程 A 和进程 B 都打开同一个文件 f,则每个进程都有自己的 struct file 结构。Linux 文件系统中的 inode 结构与磁盘上的文件一一对应。即使多个进程打开同一文件,整个内核中只有一个 inode 结构。进程 A 和进程 B 的 struct file 结构都包含一个指针指向这个 inode 结构,从而将它们关联起来。
        在 inode 结构中,有一个哈希表,用于将文件的页号映射到物理内存页。例如,进程 A 打开文件 f 并读取第 4 页时,内核会将页号 4 和对应的物理页存入哈希表。如果进程 B 后来也打开文件 f 并读取第 4 页,由于该页已加载到物理内存中,进程 B 只需建立虚拟地址到该物理页的映射即可,如下图所示:

        请注意,Linux内核在现代已经优化了哈希表,采用了Radix树和最小堆等数据结构,以提升时间效率。因此,在阅读不同版本的Linux内核代码时,需要留意这些变化。
        对于只读文件,物理页上的内容是共享的,这意味着多个进程(例如进程A和进程B)可能映射到同一个物理页上的同一虚拟内存页。但是,如果有进程要修改文件内容,该内存区域的属性会变为私有,内核则会进行写时复制操作,为需要写入文件的进程创建一个独立的副本。这样一来,一个进程的文件写入操作不会影响其他进程的读取操作。
        对于共享库文件而言,代码段通常是共享的,而数据段在发生变化时,内核会通过写时复制机制为每个进程创建一个独立的副本。这也是选择私有文件映射的原因之一。
总结来说,私有文件映射中的只读页是多个进程共享的,可写页则为每个进程单独创建副本,这些副本的创建时机仍然是在写操作时进行写时复制。

共享文件映射​


        其实我们不难发现,在私有文件映射的基础上,共享文件映射就很简单了:对于可写的页面,在写的时候不进行复制就可以了。这样的话,无论何时,也无论是读还是写,多个进程在访问同一个文件的同一个页时,访问的都是相同的物理页面。​

共享匿名映射


        之前,你可能会觉得共享匿名映射是最简单的,因为其进程共享了相同的 mmap 的返回值,看上去最直观。但实际上,从内核的角度说,它却是最复杂的一种。​原因是 mmap 并不真正分配物理内存,它只是分配了一段虚拟内存,也就是说只在 PCB 中创建了一个 vma 结构而已。这就导致 fork 在复制页表的时候,页表中共享匿名映射区域都是未映射状态。​
        如果内核未经特殊处理,在父进程因访问共享内存区域而触发缺页中断时,内核会为其分配物理页面。然而,当子进程再次访问同一共享内存区域时,内核无法直接映射到正确的物理页面。因为缺页中断仅提供当前进程身份和问题虚拟地址,这些信息不足以判断其他进程是否已经准备好共享内存。
        在早期的 Linux 内核中,在采用虚拟文件系统解决这一问题之前,是不支持共享匿名映射的。虚拟文件并不存在于磁盘上,而是内核模拟生成的,但具备自己的inode结构。这样一来,内核在创建共享匿名映射区域时,会创建一个虚拟文件,并将其与映射区域的vma(虚拟内存区域)相关联。
        当使用fork系统调用创建子进程时,子进程会复制父进程的所有vma信息。接下来的步骤与共享文件映射完全一致,无需重复详述。因此,mmap之所以功能强大,主要得益于操作系统综合使用写保护中断、缺页中断和文件机制来实现各种mmap功能。

你对 mmap 的分析很到位,特别是在处理共享内存区域时的挑战和解决方案。让我进一步补充一些相关的细节和背景信息。


mmap 的工作原理和内核处理


·共享内存区域和缺页中断


        在操作系统中,共享内存区域通过 mmap 函数创建,它允许多个进程共享同一个物理内存区域的虚拟映射。当父进程首次访问共享内存区域时,如果该区域尚未映射到物理内存中(即发生缺页中断),内核会为该进程分配一个物理页面,并建立虚拟地址到物理地址的映射关系。


·子进程的处理


        子进程通过 fork 创建时会继承父进程的地址空间,包括所有的 vma(Virtual Memory Area)信息,这些信息描述了进程的虚拟内存布局。因此,子进程可以直接访问父进程的共享内存区域,而无需重新创建或映射。


·使用虚拟文件系统


        早期的 Linux 内核不支持直接的匿名内存映射(即不映射到文件),因此使用虚拟文件系统来模拟这一过程。通过创建一个特殊的虚拟文件,并将其关联到 vma 中的映射区域,内核能够正确管理和共享这些内存区域。


·写保护中断和缺页中断


        在 mmap 过程中,内核利用写保护中断(当进程试图写入只读内存时触发)和缺页中断(当虚拟地址尚未映射到物理内存时触发)来实现对内存访问的监控和管理。这些机制保证了进程在访问共享内存时的安全性和一致性。


·mmap 的功能和优势


        mmap 提供了强大的功能,允许进程在虚拟地址空间中动态映射文件或匿名内存区域。它的实现依赖于操作系统内核综合使用了写保护中断、缺页中断和虚拟文件系统等机制。这种设计使得 mmap 能够灵活地支持多种应用场景,包括内存共享、文件映射和实现零拷贝等操作。总之,mmap 的设计不仅仅是为了提供简单的内存映射功能,而是通过操作系统内核的多种技术手段,实现了高效、安全和可靠的内存管理和共享机制。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值