内存虚拟化的目的是为进程创造独享大量虚拟内存的错觉,通过地址映射(Address Mapping)将进程的虚拟地址空间转换为硬件的物理地址。

1.1 虚拟地址空间的抽象

地址空间(Address Space)是程序可视的系统内存,包含其运行所需的代码和数据。每个程序运行在各自的地址空间中,实现了强隔离。用户进程使用的是虚拟地址(Virtual Address, VA),通过硬件(例如内存管理单元,Memory Management Unit, MMU)和操作系统协同实现的地址转换机制,将虚拟地址转换为物理地址(Physical Address, PA)。

物理地址结构与平台相关,通常不仅包括RAM,还包括I/O设备的内存映射和启动ROM。内核虚拟地址空间由内核设计,包含内核代码、数据、内核栈及可分配给用户程序的内存等;而用户进程的虚拟地址空间由内核提供,涵盖了用户代码、数据、栈和堆等。

在C语言中,变量直接声明使用栈内存,而堆内存则需要使用内存分配函数手动管理,如:

  • malloc():申请指定大小的堆内存,成功时返回地址,失败时返回NULL。
  • free():释放已申请的内存。

上述两种内存管理的区别在于:堆内存可跨越函数调用持续存在,而栈内存会在函数返回后自动释放。此外,程序所操作的API皆基于虚拟地址。

内存分配的底层通过系统调用(以Linux为例)实现:

  • brk():调整程序数据段的结束位置,以扩展或缩小堆的大小。
  • sbrk():增加或减少程序数据段的大小,返回新的数据段结束位置。

这些调用实际上是在改变堆内存结构,扩展程序可用的内存空间。此外,与内存映射I/O相关的调用有:

  • mmap():将文件或设备的部分映射到内存中。
  • munmap():解除文件或设备在内存中的映射。

1.2 地址转换方法

1.2.1 地址转换策略演变

策略 说明 硬件支持 演进 缺陷
基址+界限(Base and Limit Registers) 整块内存分配,利用基址计算PA=VA+base,且VA需小于界限 需要MMU寄存器 简单直接 内存利用率低,导致碎片
分段(Segmentation) 地址空间分段,使用段号及偏移量来定位 需要寄存器存放段信息 减少内部碎片 产生不等大小的空闲空间(外部碎片)
分页(Paging) 固定大小的内存页与页框映射 页表寄存器和地址转换单元 消除外部碎片 地址转换开销大
段页式(Segmented Paging) 多段页表减少页表大小 所需寄存器和硬件单元 不依赖段分配内存 大稀疏堆仍有页表浪费
多级页表(Multilevel Page Tables) 多级页表减少不必要空间使用 多级页表结构减少内存占用 广泛应用 访存操作多,增加耗时

1.2.2 RISC-V系统(XV6)三级页表

XV6基于RISC-V平台,定义物理地址(Physical Address, PA)为56位,虚拟地址(Virtual Address, VA)为39位,使用三级页表索引。L2页目录存有L1页表信息,而L1页表包含L0页表信息。结构类似于Unix V6,页表项包括物理页号(Physical Page Number, PPN)和权限标志位。

地址转换示意图

1.2.3 X64硬件(Linux)四级页表

在x86-64架构的Linux系统中,采用四级页表结构,包括:

  • PGD(Page Global Directory):页全局目录
  • PUD(Page Upper Directory):页上层目录
  • PMD(Page Middle Directory):页中层目录
  • PTE(Page Table Entry):页表项

地址转换过程与RISC-V平台类似,但由于x86-64架构的复杂性,页表层级更多,以提供更细粒度的内存管理。每级页表通过指针链接,逐级索引,最终获取物理地址。

1.3 缓存和页面交换

即便使用多级页表,仍然存在以下问题:

  1. 多次访存查找页表导致的耗时开销。
  2. 页表和进程占用过多内存,可能导致RAM耗尽。

通过以下存储层级方案解决:

  1. 采用**TLB(Translation Lookaside Buffer)**缓存未来访问的映射关系。
  2. 使用外部存储进行页面交换(Page Swapping),记录过多的内存页面。

1.3.1 TLB机制

TLB是一种高效缓存最近使用的页表信息的硬件装置。访存时,系统首先查找TLB,若命中(Hit),则直接使用缓存的物理地址;若未命中(Miss),则触发异常,按页表流程查找,并将新的映射关系记录到TLB中。TLB满时,替换策略决定更新内容。

1.3.2 页面交换

页面交换(Page Swapping)是将不常用的页面从内存移至磁盘的过程。当访问某页时,若该页不在内存中,则引发页错误(Page Fault),系统将该页从磁盘换入内存。在内存空间不足时,操作系统根据替换策略选择换出页面,腾出内存空间。

1.3.3 替换策略发展

策略 解释 缺点
最优替换(Optimal Replacement) 基于未来访问预测,逐出最长时间不使用的页面 无法实现,因需提前知道未来访问顺序
FIFO(First In, First Out) 逐出最先进入内存的页面 容易出现抖动,导致频繁换页
随机替换(Random Replacement) 随机选择页面替换 命中率随机,可能导致效率低下
LRU(Least Recently Used) 逐出最少使用的页面 实现成本高,需维护最近使用信息
近似LRU(Approximate LRU) 定期清除访问标记,模拟LRU 参数选择影响命中率,精度较低

1.3.4 页错误

页错误分为以下几种情况:

  • 硬错误(Hard Fault):页面在磁盘中,需要换入内存。
  • 软错误(Soft Fault):页面在内存中,但未建立映射关系。
  • 段错误(Segmentation Fault):访问了不在虚拟地址空间中的地址。

1.4 其他内存虚拟化机制

  • 惰性分配(Lazy Allocation):延迟内存分配,直到确实需要时才进行实际分配。
  • 按需清零(Zero-on-Demand):仅在写入前分配的内存区域返回已清零的内存。
  • 写时复制(Copy-on-Write, CoW)fork()时子进程和父进程共享内存,只有在写操作发生时才复制内存页。
  • 按需调页(Demand Paging):仅在需要时加载程序的代码和数据,减少不必要的内存占用。
  • 预取(Prefetching):在访问一页后,提前将下一页调入内存,提高访问效率。
  • 集中写入(Write Gathering):磁盘换入时,同时写回多个页面,提高I/O效率。
  • 内存映射I/O(Memory-Mapped I/O):将文件或设备映射到内存,延迟到真正需要时再执行I/O操作。