背景
CPU 能直接访问的存储器只有内存和处理器内的存储器。为了实现保护,需确保每个进程只访问其合法地址,这通过与只允许操作系统修改的基址寄存器和界限地址寄存器来实现。操作系统运行在内核模式下,可以无限制地访问内存。
系统从输入队列中选取一个进程装入内存,进程终止后其地址空间被释放。多数系统中用户进程被放在物理内存的任意位置。编译器将符号绑定到可重定位的地址,链接器或加载器将可重定位的地址绑定成绝对地址。绑定的时机可分为编译时(绝对代码)、加载时(可重定位代码)、执行时(绝大多数采用)。
- 逻辑地址 LA/虚拟地址 VA:CPU 所生产的地址。
- 物理地址 PA:加载到内存地址寄存器中的地址,即内存看到的地址。
- 内存管理单元 MMU:实现 VA 到 PA 的映射。
- 重定位寄存器:即基地址寄存器,用户进程生产的地址交送内存前加上重定位寄存器的值。
调度程序会在上下文切换时设置重定位寄存器和界限地址寄存器,由于 CPU 产生的每个地址都需要进行比较核对,由此可以实现程序和数据的保护。
- 动态加载:子程序只有在调用时才被加载。
- 动态链接:将链接延迟到运行时,执行时检查所需是否在内存中,如果不在就装入,此后用子程序地址替代存根。通常检查等工作需要操作系统的帮助。
交换
进程可以暂时从内存中交换到备份存储上。在优先级调度算法中,低优先级进程可以被暂时患处,等待高优先级执行完后再换入。
绑定方式决定了一个被换出的进程是否需要被换回它原来所占有的内存空间。
交换系统的上下文切换时间较长,同时若要强行交换一个正在 I/O 的进程则需要设置缓冲区。因此现代很少使用标准交换。
连续内存分配
操作系统通常放在内存的低地址区域。本节考虑如何在每个进程位于一个连续的内存区域的情况下进行内存分配。
- 分区:将内存分为多个固定大小的分区。
- 可变分区:OS 用一张表记录哪些内存已经被占用,有新进程需要内存时查找足够大的孔。
- 首次适应:分配第一个足够大的孔。
- 最佳适应:分配最小的足够大的孔。
- 最差适应:分配最大的孔。
首次适应和最佳适应都会产生外部碎片,分区会产生内部碎片。外部碎片虽然可以通过紧缩解决,但开销较大且并不总是可行,因此我们引入允许物理地址空间为非连续的分配方法:分页和分段。
分页
传统分页是由硬件处理的,但最近的设计中由硬件和 OS 配合处理。选取一个固定的大小为页大小,虚拟内存分为此固定大小的页,物理内存也分为此固定大小的帧,备份存储也是如此。执行需要时将页从备份存储调入到可用的内存帧中。
CPU 生成的地址分为两个部分:页号和页内偏移。页号是页表中的索引,页表项中包含了每页所在物理内存的地址。操作系统中通常用帧表来记录帧的使用情况。
绝大多数操作系统中每个进程拥有一个页表,页表指针存放在进程控制块中,页表存放在内存中并通过 PTBR 来指向,但这样意味着一次访问需要两次访存。因此设置了转换表缓冲区 TLB 作为页表的快速缓冲,TLB 条目中包含了虚页号作为键、帧号作为值,有时也包含地址空间标识符 ASID 来标识进程,使得 TLB 可以同时包括多个不同进程的条目。
为了实现内存保护,页表项中会设置有效位来表示页是否在进程的逻辑地址空间内,设置读写位来定义一个页是可读写的还是只读的。有时会设置页表长度寄存器 PTLR 来表示页表的大小,因为一个进程很少会使用其所有的地址空间。
分页时可以很方便地实现共享公共代码。进程可以使用同一块可重入代码,每个进程拥有其自己地寄存器副本和数据存储。
在具有较大地逻辑地址空间的系统中直接用连续的物理地址空间存储页表是不现实的。
考虑将页表划分为更小的部分。多级分页算法将页表再分页,但对 32 位以上情形显得过于复杂;哈希页表用虚拟页号的哈希值来检索页表,此时需要在页表项中记录虚页号,不匹配时需进行前向探测;群集页表类似于哈希页表,只不过每个条目包含多页,这对于稀疏地址空间特别适用。
反向页表对于每个物理内存帧设置一个条目,包含其对应的虚页号以及进程的信息,通过哈希来加速查找。显然,这种方式难以支持内存共享。
分段
分页管理方式背离了实际用户程序编写时的直觉。分段管理中,逻辑地址空间由一组段组成,每个段有名称和长度,逻辑地址由段号和段内偏移组成的有序对表示。
分段的映射过程通过段表实现,每个段表条目包含该段的段基地址和段界限。
Pentium 中的分段与分页
在 Pentium 中,CPU 产生逻辑地址传送给分段单元,生成线性地址后交给分页单元,最后转换成物理地址。可见,MMU 的工作在这里由分段单元和分页单元来完成。
Pentium 允许一个段最大 4GB,每个进程最多 16384 个段。进程的逻辑地址空间分为两部分,由本地描述符表 LDT 标识的私有逻辑地址空间,和由全局描述符表 GDT 标识的共享地址空间。LDT 和 GDT 中每个条目包括一个段的详细信息。
逻辑地址由 16 位的选择器和 32 位的偏移构成。选择器包括 13 位的段号,1 位 GDT/LDT 标识,2 位的保护信息。机器拥有 6 个段寄存器。
根据段寄存器指向的 LDT 或 GDT 中的条目内容,将段基地址和段内偏移相加产生 32 位的线性地址。
Pentium 中页大小有 4KB 和 4MB 两种,前者采用二级页表管理。第一级页表称为页目录,由 CR3 寄存器指向,如果页目录中的一个条目具有 PageSize 有效标志,那么页目录直接指向一个 4MB 的页帧。
若页表被交换到了磁盘上,那么页目录条目的有效位可以标识这一状态,同时用其他 31 位来表示页表在磁盘上的位置。
总结
本章中介绍了连续分配、分页、分段、分段分页结合等内存管理方式,在硬件支持、性能、碎片、重定位、共享、保护等方面均有诸多不同。