页中断:fork、mmap背后的保护神

322 阅读3分钟

页中断和普通的中断一样,它的中断服务程序入口也在 IDT 中,但它是由 MMU 产生的硬件中断。页中断有两类重要的类型:写保护中断和缺页中断。正是这两类中断在整个系统的后台默默地工作着,就像守护神一样支撑着内存系统正常工作。

根据中断来源的不同,页中断大致可以分为以下几种类型:

image.png

PCB:操作系统为每个进程提供了一个进程管理的结构,Linux 系统上,PCB 就是task_struct 这个结构体。

vm_area_struct:简称vma,描述线性空间已分配的内存区域的结构。

在操作系统内核里,fork 的第一个动作是把 PCB 复制一份,但类似于物理页等进程资源不会被复制。

fork 的第二个动作是复制页表和 PCB 中的 vma 数组,并把所有当前正常状态的数据段、堆和栈空间的虚拟内存页,设置为不可写,然后把已经映射的物理页面的引用计数加 1。

image.png

在上图中,物理页括号中的数字代表该页被多少个进程所引用。Linux 中用于管理物理页面,和维护物理页的引用计数的结构是 mem_map 和 page struct。

execve 的实现并不负责将文件内容加载到物理页中,它只建立了这种文件 section,与内存区域的映射关系就结束了。真正负责加载文件内容的是缺页中断。

可执行程序的加载不是一次性完成的,而是由缺页中断根据需要,将文件的内容以页为单位加载进内存的,一次只会加载一页。

mmap

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

  • 私有匿名映射,用于分配堆空间;
  • 共享匿名映射,用于父子进程之间通讯;
  • 私有文件映射,用于加载动态链接库;
  • 共享文件映射,用于多进程之间通讯。

私有匿名映射,在缺页中断的处理过程,会通过 do_anonymous_page 函数申请一块全零的物理页,并建立虚拟地址到物理页的映射,以达成分配内存的目标;

私有文件映射,则借助文件的 inode 结构共享文件的物理缓存页,当发生写操作时,则会出现写时复制,从而保证每一个进程中都有自己的副本;

共享文件映射,在私有文件映射的基础上,只取消了写时复制,这样一个进程就可以看到其他进程对这个页的修改了;

共享匿名映射,借助了虚拟文件系统。内核在父子进程间,使用自己创建的虚拟文件和共享文件映射,来实现共享匿名映射。

mmap 并不真正分配物理内存,它只是分配了一段虚拟内存,也就是说只在 PCB 中创建了一个 vma 结构而已。这就导致 fork 在复制页表的时候,页表中共享匿名映射区域都是未映射状态。

mmap 的功能之所以十分强大,主是因为操作系统综合使用写保护中断、缺页中断和文件机制来实现 mmap 的各种功能。


此文章为7月Day10学习笔记,内容来源于极客时间《编程高手必学的内存知识》