图片无法显示,请右键点击新窗口打开图片

BiscuitOS 内存管理之分页大专题订阅入口

在 Linux 内核中,存在一种特殊的物理内存: “ZERO PAGE(零页)”,正如它的名字所言,零页是一个内容全部为 0 的物理页. 在 Linux 内存管理里,零页的用途很广,多被用于优化和安全目的. ZERO PAGE 支持多个进程的虚拟内存同时映射,也支持同一个进程的多个虚拟区域映射到其上, ZERO PAGE 只能读而不能写,如果进程对 ZERO PAGE 发起写操作会引起缺页异常,异常处理会为其新分配一个物理页,然后将新写的内容写到新的物理页上. 从优化内存和安全的角度看,ZERO PAGE 具有如下功能:

  • 加速分配: 当进程请求新的内存页时,如果这些页需要初始化为零,内核可以将这些请求映射到已经存在的、内容全为零的 ZERO PAGE. 这种方法避免了立即分配和清零新的物理内存页,从而节省了内存和处理时间
  • 减少物理内存使用: 多个进程可能会共享相同的 ZERO PAGE,因为它们内容完全相同, 这种共享减少了物理内存的使用,因为不需要为每个进程单独分配内存.
  • 防止数据泄露: 新分配的内存通常需要清零,以防止程序意外访问到先前分配给其他进程的内存中的数据. 通过映射到已清零的 ZERO PAGE,内核能够确保新分配的内存不会包含任何遗留数据,从而防止潜在的数据泄露

当进程只需对虚拟内存进行读操作,那么系统会将这些虚拟内存都映射到 ZERO PAGE 上,这样既可以加速内存的访问,也节省了物理内存,这样做的前提是内核将映射到 ZERO PAGE 的页表都修改为只读或者写保护. 但当进程需要对虚拟内存执行写操作时,MMU 检查到权限异常而触发缺页异常,在缺页异常处理函数里为虚拟内存分配新的物理页,然后建立页表将虚拟内存映射到物理内存上,待缺页异常返回之后,进程重新执行发送缺页的指令,那么此时进程可以正确的将数据写入到新的物理内存,那么接下来进程所有的读写操作都来自新物理页.

如果进程的虚拟区域映射到 ZERO PAGE 之后,进程调用 fork 系统调用生成子进程,那么子进程同样也会继承这份映射关系,只要子进程不对虚拟内存发起写操作,那么所有的读操作都来自 ZERO PAGE,并且不会引起缺页. 一旦子进程对虚拟内存发起写操作,那么会触发缺页,此时的行为正式 COW(Copy-On-Write), 但与一般的 COW 不同的是其不需要拷贝父进程虚拟区域的内容,而是直接分配一个新的物理页,并建立页表映射到新物理页即可.

ZERO PAGE 仅仅支持匿名内存,另外零页的大小可以是 PAGE_SIZE,也可以是 PMD_SIZE. PMD_SIZE 的情况适用于匿名透明大页场景. 系统是否支持零页可以通过一定的手段进行控制,但不同的架构可以实现存在差异,如下图所示:

图片无法显示,请右键点击新窗口打开图片

BiscuitOS 内存管理之分页大专题订阅入口

图片无法显示,请右键点击新窗口打开图片