本节我们讨论虚拟内存在操作系统和硬件中的实现,重点解释基本原理和机制,然后在之后的章节中深入讨论细节和设计空间。
典型的基于分页的虚拟内存系统
大多数虚拟内存系统采用分页策略,虚拟地址空间呗划分为页和页帧(page frame,或简称为帧),页是虚拟内存管理的最小单位,而页帧是物理内存管理的最小单位。现代操作系统普遍采用按需分页,也就是说只有用户需要时才会分配页面。
现在的CPU普遍采用4 KB的页面,但也开始利用超级页来缓解虚拟内存系统中的压力,直到今天如何有效利用超级页仍然是一个活跃的研究领域。页面大小也是一个关键的实现细节,Linux的mprotect
或Windows的VirtualProtect
等syscall需要以页面大小为粒度来进行内存管理。下图展示了页面大小如何影响地址转换的过程。
页表
在一个基于分页的实现中,每个进程的虚拟到物理的映射集合被存储在一个被称为页表的数据结构中,有许多方法可以实例化页表结构,我们在下面介绍其中的一些方法。
页表的基本单元是页表项,存储了一个特定页的虚拟到物理地址转换信息的所有相关信息,如上图例子所示。页表必须能覆盖整个虚拟地址空间,但为了减少页表占用的空间,现在最流行的设计是分层或多级页表,如下图所示。
注意中间的页表项也是有权限和状态位的,可能在中途就触发page fault。页表的层数在不同的ISA中可能也是不一样的,并根据地址空间的大小来决定,例如32位x75采用两级或三级页表,将32位虚拟地址转换为40或52位物理地址,而64位x86和ARM架构采用四级页表,将48位虚拟地址转换为52位物理地址。多级页表也能很好地实现混合页面大小。
TLB
如果没有TLB,虚拟内存实现的成本会很高,内存带宽的要求将提升2-5倍。不过为了保持速度,TLB本身不能使很大的结构,许多处理器也会使用类似缓存的层次结构来实现TLB。
Page and Segmentation Faults
Page fault一般分为两类,次要和主要的。次要的page fault表示页帧存在于物理内存中,但是在页表中没有对应的页表项,或者没有设置合适的权限。这种可能发生在如下一些情况:页面已经分配但还没有被访问、页帧被赋予了多次映射(经常发生在共享库或进程间共享内存中)、写时复制等。主要的page fault表示所需的数据不在内存中,需要从磁盘上获取,访问该数据会产生主要的page fault,并将数据带入内存。当page fault发生时,CPU通常通过一些控制状态寄存器来协助操作系统定位问题,且这些细节是和ISA的设计紧密联系的。
Segmentation fault表示访问请求的虚拟地址是非法的,如用户可能访问一个未分配过的虚拟地址,或者打破了权限,这些通常是由于程序员的错误造成的,唯一安全的做法是终止进程。