虚拟存储器
1. 概述
1.1. 定义
- 定义: 虚拟存储器(Virtual Memory)是一种将主存 (Main Memory) 用作辅助存储器 (Secondary Storage,如磁盘、闪存) 的高速缓存(Cache)的技术。它构成了存储层次结构中的一级。
- 历史动机 (早期): 主要是为了消除物理主存容量的限制,允许单个用户程序使用比实际物理内存更大的地址空间。这解放了程序员从手动管理程序覆盖段(overlays)的复杂任务中。
- 现代动机 (主要): 经过50多年的发展,虚拟存储器的主要设计动机已转变为在多虚拟机(或进程)之间实现有效且安全地共享物理存储器。
1.2. 核心机制:地址转换 (Address Translation)
- 虚拟地址 (Virtual Address): 处理器(CPU)产生或程序使用的地址,存在于每个程序(或进程/虚拟机)独立的地址空间 (Address Space) 中。
- 物理地址 (Physical Address): 主存储器(RAM)的实际硬件地址。
- 地址转换/地址映射: 虚拟存储器的核心功能,是指通过硬件和软件(通常是操作系统)的协作,将程序使用的虚拟地址转换为对应的物理地址的过程。
- 目的: 实现程序各自独立的虚拟地址空间到共享物理内存的映射,并在此过程中加强程序之间的保护 (Protection)。
- 保护: 一组机制,用于确保在共享处理器、主存、I/O设备的多个进程(或虚拟机)之间,任何一个进程都不能故意或无意地读写属于其他进程的数据,同时也隔离用户进程与操作系统。
- 局部性原理: 虚拟存储器利用程序的局部性原理(时间局部性和空间局部性),使得操作系统只需要将程序当前活跃使用的部分加载到主存中,而将不活跃的部分保留在辅助存储器上。
1.3. 虚拟存储器的优势
- 共享 (Sharing): 允许多个程序/虚拟机高效、安全地共享宝贵的物理主存资源和处理器时间。
- 保护 (Protection): 通过地址转换机制,隔离不同程序的地址空间,防止相互间的非法访问和破坏。
- 扩展地址空间 (Large Address Space): 使得单个程序可以拥有一个比实际物理内存大得多的虚拟地址空间,简化了程序设计。
- 重定位 (Relocation): 简化程序的加载和链接过程,允许程序被加载到物理主存中的任何可用位置,而无需在编译或链接时固定其物理地址。通过分页机制,程序可以分散加载到多个不连续的物理页框中。
2. 页式虚拟存储器 (Paged Virtual Memory)
2.1. 地址结构与映射单位
- 存储单元划分: 虚拟地址空间和物理地址空间都被划分为固定大小的块。
- 虚拟地址空间的块称为页 (Page)。
- 物理地址空间的块称为页框 (Page Frame) 或物理页。通常页和页框的大小相等。
- 地址构成:
- 虚拟地址: 由虚页号 (Virtual Page Number, VPN) 和页偏移 (Page Offset) 组成。
- 物理地址: 由物理页号 (Physical Page Number, PFN) 和页偏移 (Page Offset) 组成。
- 地址转换过程: 虚拟地址的页偏移部分直接成为物理地址的页偏移部分,不发生改变。地址转换的核心是将虚页号 (VPN) 转换为物理页号 (PFN)。
- 页大小: 由页偏移字段的位数决定(例如,12位页偏移对应 212 = 4096字节 = 4KB 的页大小)。典型的页大小在4KB到16KB之间,但也存在更大(如32KB-64KB,甚至MB、GB级)或更小(如1KB)的页面。
- 虚拟/物理页数: 虚拟地址空间通常包含远多于物理页框数量的虚拟页,为程序提供了仿佛拥有巨大内存的假象。
2.2. 缺页代价与设计考量
- 缺页 (Page Fault): 当程序访问一个虚拟地址,而该虚拟地址对应的页当前不在主存中时发生。
- 缺页代价极高: 由于需要从辅助存储器(如磁盘)读取页面,一次缺页处理通常需要数百万个时钟周期,远高于Cache缺失的代价(主存访问比磁盘快约100,000倍)。
- 降低缺页率的关键设计因素:
- 较大的页: 分摊高昂的辅助存储器访问时间,并更好地利用空间局部性。
- 全相联放置 (通过页表实现): 允许虚拟页映射到主存中的任何物理页框。这种灵活性使得操作系统可以自由选择替换策略,以最小化未来的缺页。
- 软件处理缺页: 缺页处理的复杂性较高,且其时间开销(与磁盘访问相比)相对较小,因此通常由操作系统通过软件来处理,可以使用更复杂的替换算法。
- 写回机制 (Write-Back Policy): 由于写辅助存储器的代价极高且耗时,写直达机制在虚拟存储器中不适用。必须使用写回机制,修改只发生在主存,只有在页被替换出时才写回(如果它已被修改)。
2.3. 段式管理 (Segmentation) - 与分页的对比
- 定义: 另一种虚拟地址到物理地址的映射策略,使用可变长度的块。逻辑地址由段号 (Segment Number) 和段内偏移 (Segment Offset) 组成。段号通过段表映射到物理基地址,加上偏移得到最终物理地址。
- 特点:
- 段大小可变,需要硬件进行边界检查。
- 主要用于提供更灵活的保护和逻辑上的地址空间共享。
- 缺点:
- 程序员和编译器需要显式地处理两部分地址,管理跨段的数据和指针复杂。
- 硬件实现通常比分页复杂。
- 作为地址空间扩展方式尝试时,因对程序员不友好而失败。
- 现代“段”结构: 许多现代体系结构使用固定大小的大块(有时也称为“段”),对用户程序不可见,主要用于简化操作系统和用户进程之间的保护,并提高分页实现的效率。这种结构比可变长段式管理简单得多。
3. 页的存放和查找:页表 (Page Table)
3.1. 页表的作用与结构
- 作用: 实现虚拟地址到物理地址的转换。由于页的全相联放置(理论上),无法像Cache那样通过硬件并行比较所有可能的物理位置来查找,因此需要一个索引结构。
- 页表定义: 一个数据结构,通常存储在主存中,用于保存虚拟页到物理页框的映射关系。
- 索引方式: 使用虚拟地址中的虚页号 (VPN) 作为索引,定位页表中的对应条目。
- 页表项 (Page Table Entry, PTE): 页表中的每个条目对应一个虚拟页,包含关键信息:
- 有效位 (Valid Bit): 指示该虚拟页当前是否在主存中。0表示不在,需从辅助存储器加载(缺页)。
- 物理页号 (Physical Page Number, PFN): 如果有效位为1,存储该虚拟页映射到的物理页框的起始地址(高位)。
- (可能)磁盘地址:如果有效位为0,可能存储该页在辅助存储器中的位置。
- 其他控制位:如保护位(读/写/执行)、脏位 (Dirty Bit)、引用位 (Reference Bit/Use Bit)等。
- 无需标记位: 由于页表理论上包含了每个可能的虚拟页的条目,通过虚页号直接索引即可找到对应信息,不需要像Cache那样进行Tag匹配。
- 页表寄存器 (Page Table Register): 硬件包含一个寄存器,存储当前活动进程的页表在物理主存中的起始地址。
3.2. 进程与页表管理 (硬件/软件接口)
- 进程状态 (Process State): 一个进程(或虚拟机)的完整状态包括其页表、程序计数器(PC)和寄存器的内容。
- 进程切换 (Context Switch): 当操作系统将CPU分配给另一个进程时,需要保存当前进程的状态,并加载新进程的状态,包括将页表寄存器指向新进程的页表。
- 操作系统职责: 操作系统负责物理主存的分配和管理,并更新各进程的页表,确保不同进程的虚拟地址空间映射到互不冲突的物理页框上。
- 保护: 每个进程拥有独立的页表,且页表由操作系统管理,这是实现进程间内存保护的基础。
3.3. 缺页故障 (Page Fault) 处理流程
- 检测: CPU访问虚拟地址时,MMU查找页表,若对应PTE有效位为0,硬件检测到缺页。
- 异常: CPU硬件触发一个缺页异常,将控制权转移给操作系统内核中预设的缺页处理程序。
- 定位磁盘上的页: 操作系统根据导致缺页的虚拟地址(通过页表或其他数据结构)找到该页在辅助存储器(通常是磁盘的交换区 (Swap Space))上的位置。交换区是操作系统为进程所有虚拟页预留的磁盘空间。
- 选择物理页框替换 (Victim Selection): 如果主存已满,操作系统必须选择一个物理页框来存放新页。选择哪个页框被替换出去(受害页)对性能至关重要。
- 替换策略: 大多数操作系统采用最近最少使用 (LRU) 算法或其高效近似实现,目标是选择近期最不可能被访问的页。
- 硬件辅助: 一些硬件提供引用位 (Reference Bit/Use Bit),当页被访问时硬件置位,操作系统定期清零并检查此位来估算页的使用频率。
- 写回脏页 (Write-Back Dirty Page): 如果被选为替换的页是脏页 (Dirty Page)(PTE的脏位为1,表示在主存中被修改过),操作系统必须先将其内容写回到辅助存储器(交换区),以防止数据丢失。
- 加载请求的页: 操作系统发起I/O操作,将导致缺页的页从辅助存储器读取到选定的物理页框中。
- 更新页表和TLB: I/O完成后,操作系统更新该页对应的PTE(设置有效位为1,填入PFN,清脏位等)。同时,使TLB中可能存在的与被替换物理页框相关的旧条目失效。
- 恢复进程执行: 操作系统恢复原先导致缺页的进程状态(通常包括PC),让其重新执行导致缺页的那条指令。此时,该页已在主存中,访问将成功。由于磁盘访问耗时巨大,操作系统通常会在等待I/O完成期间调度其他进程执行。
3.4. 页表大小与优化技术
- 问题: 简单的、为每个进程维护的线性页表会随着虚拟地址空间的增大(特别是64位系统)而变得巨大,消耗大量物理内存。
- 减少页表存储量的主要技术:
- 界限寄存器 (Bounds Register): 限制页表大小,仅为进程实际使用的虚拟地址范围分配页表空间,并允许页表随使用量增长。适用于地址空间主要向一个方向增长的情况。
- 双向/分段页表 (Two-Level Growth / Segmented Page Table): 将地址空间划分为两部分(如栈和堆),分别使用独立的、可增长的页表。地址高位决定使用哪个页表。对应用程序不可见。对稀疏地址空间效果不佳。
- 反置页表 (Inverted Page Table): 页表条目数量与物理页框数成正比,而不是与虚拟页数成正比。使用物理页框号(或通过哈希)索引,查找特定虚页对应的物理页框较复杂。
- 多级页表 (Multi-level Page Table): 将页表组织成树状结构。顶层页表映射虚拟地址空间中的大块(段),其条目指向下一级页表。只有当一个大块包含的页面被实际使用时,才分配相应的下一级页表。适用于稀疏地址空间,但增加了地址转换时的内存访问次数。
- 页表的页化 (Paging the Page Table): 允许页表本身也存储在虚拟地址空间中,并可以像普通数据页一样被换出到辅助存储器。需要确保操作系统核心页表始终驻留在物理内存中,以避免递归缺页死循环。
4. 写操作的处理
4.1. 写回机制 (Write-Back) 的必要性
- 原因: 对辅助存储器(尤其是磁盘)的写操作延迟高达数百万时钟周期。像Cache那样使用写缓冲区来隐藏写延迟的写直达机制在虚拟存储器中完全不可行。
- 策略: 虚拟存储系统必须使用写回机制。写操作首先修改主存中的页。只有当该页因为替换或其他原因需要被移出主存时,才考虑将其内容写回到辅助存储器。
4.2. 脏位 (Dirty Bit) (硬件/软件接口)
- 作用: 在页表项 (PTE) 中引入一个脏位,用于追踪自从页被加载到主存后,是否对其进行过写操作。
- 机制: 当对主存中某个页进行写操作时,硬件自动将该页对应的PTE中的脏位置1。
- 优化写回: 当操作系统需要替换一个物理页时,检查其对应PTE的脏位:
- 脏页 (Dirty Page, Dirty Bit = 1): 必须将页的内容写回到辅助存储器(交换区),因为主存中的版本比辅助存储器中的新。
- 干净页 (Clean Page, Dirty Bit = 0): 无需写回,可以直接丢弃该页的内容,用新页覆盖该物理页框。
- 效率: 脏位显著提高了写回机制的效率,避免了不必要的辅助存储器写操作,特别是在替换干净页时。
5. 加快地址转换:快表 (Translation-Lookaside Buffer, TLB)
5.1. TLB 的需求与定义
- 性能问题: 如果每次内存访问都需要访问主存中的页表进行地址转换,再访问主存获取数据,将导致每次访存至少两次主存访问,严重影响性能,Cache的存在也会变得意义不大。
- 局部性: 页表的访问也遵循局部性原理。程序在一段时间内倾向于访问同一组页面。
- TLB 定义: 为了加速地址转换,现代处理器包含一个特殊的、硬件管理的高速Cache,用于存储最近使用过的虚拟地址到物理地址的转换信息。这块Cache称为快表 (Translation-Lookaside Buffer, TLB)。
5.2. TLB 的工作原理与内容
- TLB 结构: TLB包含多个条目,每个条目缓存一个虚拟页到物理页的映射以及相关权限信息。
- 标记 (Tag): 通常是虚页号 (VPN) 或其一部分,用于在TLB中查找。
- 数据 (Data): 对应的物理页号 (PFN)。
- 状态位: 包括有效位 (Valid bit)、保护位 (Protection bits: Read/Write/Execute)、脏位 (Dirty bit)、引用位 (Reference bit)、进程ID (ASID)等,这些是PTE中关键信息的副本。
- 访问流程:
- CPU产生虚拟地址。
- MMU首先并行使用VPN在TLB中查找匹配项。
- TLB 命中 (TLB Hit): 如果找到匹配且有效的条目,且权限允许,MMU直接从TLB获取PFN,组合页偏移形成物理地址,进行Cache/主存访问。硬件可能更新TLB项的引用位/脏位。
- TLB 未命中 (TLB Miss):
- 情况一 (仅转换缺失/TLB重填): 页在主存中(PTE有效)。硬件(或软件)执行页表遍历,找到PTE,将其加载到TLB中(可能替换旧项),然后重新执行原指令。
- 情况二 (真正缺页): 页不在主存中(PTE无效)。TLB Miss导致缺页异常,由操作系统处理。
- TLB 缺失处理: 可以由硬件状态机或操作系统软件处理。MIPS等早期RISC常采用软件处理。软件处理流程通常包括保存状态、查找页表、加载PTE到TLB、恢复状态、重新执行指令。
5.3. TLB 典型参数与设计
- 典型值: 大小通常为16-512个条目;命中时间0.5-1时钟周期;缺失代价(仅转换缺失)10-100时钟周期;缺失率0.01%-1%。
- 设计考量:
- 相联度: 有系统采用小型全相联TLB(命中率高,但硬件复杂),也有采用容量大、相联度较低的TLB(如组相联)。
- 替换策略: 硬件实现LRU昂贵,TLB常采用随机替换或简化的近似LRU。
- 写回: TLB修改(脏位/引用位)通常采用写回策略,只在TLB项被替换时才写回对应的PTE,以减少对页表的写操作。
5.4. FastMATH TLB 示例
- 参数: 16个全相联条目,指令和数据访问共享,32位地址(20位VPN,12位偏移),4KB页大小。TLB项包含VPN, PFN, 有效位, 脏位等。TLB缺失由软件处理。
- 软件TLB缺失处理简述: 硬件将缺失的VPN存入特殊寄存器并产生异常。操作系统TLB缺失处理程序运行,通过页表寄存器和VPN查找页表,找到PTE,使用特殊指令将其加载到TLB的随机条目中,然后返回并重新执行原指令。若PTE无效则触发真正的缺页异常。
6. 集成虚拟存储器、TLB 和 Cache 的协同工作
6.1. 存储器层次结构操作
- 协同工作: 虚拟存储器和Cache系统形成多级层次结构。数据的存在遵循包含性原则:如果数据在Cache中,则必然在主存中;如果虚拟页在主存中,其映射信息可能在TLB中缓存。
- 操作系统管理: OS管理主存与辅助存储器之间的页交换。当一个页被换出到磁盘时,操作系统需要通知硬件(或通过页表/TLB修改)使其在Cache中的副本失效(如是脏页需先写回主存)。
- 访问路径:
- 最佳: 虚拟地址 -> TLB命中 -> 物理地址 -> Cache命中 -> CPU。
- 最坏: 虚拟地址 -> TLB缺失 -> 页表遍历(可能导致缺页) ->(若页在主存)PTE加载到TLB -> 物理地址 -> Cache缺失 -> 主存访问 -> 数据加载到Cache -> CPU。
6.2. Cache 的寻址方式 (物理 vs. 虚拟)
- 物理寻址 Cache (Physical Addressed Cache, PIPT): Cache使用物理地址进行索引和标记匹配。地址转换(TLB查找)必须在Cache访问前完成。简单,无别名问题。
- 虚拟寻址 Cache (Virtually Addressed Cache, VIVT): Cache使用虚拟地址进行索引和标记匹配。地址转换在正常Cache访问路径外。访问速度可能更快。存在别名 (Aliasing) 问题(不同虚拟地址映射同一物理页,可能导致Cache不一致)和同义词问题(同虚拟地址映射不同物理页,上下文切换需处理)。
- 虚拟索引物理标记 Cache (Virtually Indexed, Physically Tagged, VIPT): Cache使用虚拟地址索引,物理地址标记。是PIPT和VIVT的折中。地址转换与Cache索引可并行开始。可以避免简单的别名问题(如果索引位完全在页偏移内),兼具性能和简单性。FastMATH Cache(16KB页时)采用了这种方式。
7. 虚拟存储器中的保护 (Protection)
7.1. 核心功能
- 最重要的功能是允许多个进程安全共享主存,同时保护进程和操作系统免受非法访问。
- 目标是防止一个进程(无论有意或无意)读写另一个用户进程或操作系统的地址空间。TLB中的写访问位是基本保护机制之一。
7.2. 硬件/软件接口提供的保护能力
操作系统依赖硬件提供的以下基本能力实现虚拟存储保护: 1. 至少两种处理器模式: 用户模式和管理模式(内核模式)。区分运行用户代码还是OS代码。 2. 用户对部分状态的只读访问: 用户进程可读但不可写的处理器状态(如用户/管理模式位、页表指针、TLB内容)。操作系统通过特殊指令(仅在管理模式下可用)修改这些状态。 3. 模式切换机制: * 用户态 -> 管理态 (System Call): 用户程序执行特殊指令触发异常,转移控制权到OS管理代码。硬件保存用户状态(如PC到EPC)并进入管理态。 * 管理态 -> 用户态 (Return from Exception): OS完成服务后,执行异常返回指令,恢复用户模式和用户状态。
7.3. 进程间数据保护与受控共享
- 隔离: 每个进程有独立的虚拟地址空间。操作系统通过将不同进程的虚拟页映射到不相交 (disjoint) 的物理页来隔离它们。用户进程不能修改页表,保证了隔离的安全性。页表本身通常位于操作系统的保护地址空间内。
- 受控共享: 进程间共享内存需要操作系统协助。OS可以在不同进程的页表中创建指向同一个物理页的PTE。
- 写保护: 使用PTE中的写访问位/写保护位,限制共享页的访问权限(如只读)。只有OS能修改这些权限位。
- 权限位在TLB中: 为加速检查,页的访问权限信息也包含在TLB项中,每次TLB命中时由硬件检查。
7.4. 上下文切换与TLB管理
- 问题: 进程切换时,TLB中可能包含前一个进程的虚拟到物理映射。如果不同进程使用相同虚拟地址映射到不同物理页,直接使用旧的TLB条目会导致错误。
- 传统方法: 上下文切换时清除(flush)TLB中的所有条目。效率低,因为新进程可能需要重新加载它自己的常用映射。
- 现代优化:进程标识符 (Process Identifier, PID) / 地址空间标识符 (Address Space Identifier, ASID):
- 在TLB项中添加ASID字段,唯一标识属于哪个进程的映射。
- TLB命中需要同时匹配虚页号和ASID。
- 优点: 不同进程的TLB条目可以共存,上下文切换时无需完全清除TLB(只需更新ASID寄存器)。
- Cache中的类似问题: Cache也可能在进程切换时包含前一个进程的数据,根据Cache寻址方式(物理/虚拟)有不同的处理和优化方法(如使用PID)。
8. 处理TLB缺失和缺页的细节
8.1. TLB缺失的两种可能性
当TLB未命中时,可能出现两种情况: 1. 页在主存中: 对应的PTE在页表中有效,只需将PTE加载到TLB(转换缺失/TLB重填)。 2. 页不在主存中: 对应的PTE在页表中无效,需要操作系统处理(真正缺页)。
8.2. MIPS中TLB缺失与缺页的处理流程
- TLB缺失 (软件处理):
- 硬件将缺失虚页号存入
BadVAddr
等特殊寄存器,触发TLB缺失异常。 - 控制权转到操作系统TLB缺失处理程序(通常有专用入口以加速)。
- 处理程序从页表读取对应PTE,通过特殊指令(如MIPS的
tlbwr
)将PTE装入TLB的随机位置。 - 返回并重新执行原指令,此时应TLB命中。
- (优化)为提高常见转换缺失速度,MIPS软件处理程序不检查PTE有效位,若无效则下次重新执行时触发真正的缺页异常。
- 硬件将缺失虚页号存入
- 缺页异常:
- 若页表项指示页不在主存,触发缺页异常(可能是TLB缺失处理后重试触发,或直接发生)。
- 控制权转到操作系统通用异常处理程序。
- 操作系统保存进程完整状态。
- 处理缺页(定位磁盘页,选择替换页并写回如果脏,从磁盘读入新页)。
- 在等待磁盘I/O期间通常调度其他进程。
- I/O完成后,恢复原进程状态,重新执行引发缺页的指令。
8.3. 指令可重启性 (Restartable Instructions)
- 数据访问缺页的复杂性: 发生在指令中间,异常前指令未结束,异常后需从头或中断点恢复执行。
- MIPS的实现: MIPS指令简单(单周期写一个结果),易于实现可重启:只需阻止指令完成写操作,异常处理后从头重新执行即可。
- 复杂指令集挑战 (如x86): 某些指令(如块移动)可能访问/修改多个内存位置,在执行中途缺页难以从头重启。需要保存更多指令内部状态,并在异常处理后从中断点继续执行,需要硬件和软件的精细协调。
8.4. 异常处理的保护 (硬件/软件接口)
- 嵌套异常: 异常处理期间可能发生其他异常。硬件需防止状态丢失。
- 机制: 硬件提供机制(如管理模式位、禁止/使能异常信号)来临时屏蔽其他异常,操作系统在处理第一个异常时保存关键状态(如EPC、Cause寄存器等)后再重新使能异常。MIPS等体系结构提供特殊控制寄存器协助异常处理。
9. 虚拟化相关概念
9.1. 虚拟化与影子页表 (Shadow Page Table)
- 背景: 客户OS维护客户页表 (GVA -> GPA)。VMM需要将GPA映射到HPA。
- 影子页表: VMM维护的表,直接映射客户虚拟地址 (GVA) 到主机物理地址 (HPA),硬件TLB直接使用影子页表。
- 同步: VMM通过捕获客户OS对自身页表的修改(如写保护客户页表并trap),更新影子页表,确保一致性。
9.2. I/O 虚拟化
- 挑战: 共享物理I/O设备复杂,需要支持大量不同OS的设备驱动。
- 一种方法: VMM为客户机提供通用虚拟设备接口,由VMM管理实际物理I/O操作。
9.3. 虚拟存储器的虚拟化层次
- 在虚拟化中存在多层地址:客户虚拟地址 (GVA) -> 客户物理地址 (GPA) -> 主机物理地址 (HPA)。
- 实现: 硬件支持多级转换(如x86扩展页表EPT/NPT)或VMM通过软件/影子页表管理多层映射。
10. 虚拟存储器小结与性能考量
10.1. 虚拟存储器核心技术总结
- 目的: 管理主存/磁盘缓存,扩展地址空间,提供进程保护/共享。
- 核心技术: 分页,页表,地址转换,缺页处理,写回机制,脏位,TLB。
10.2. 理解程序性能
- 抖动 (Thrashing): 程序工作集远大于物理内存时发生,频繁页交换导致性能崩溃。解决方法是增加内存或改善程序局部性(减小工作集)。
- TLB 缺失: 常见的性能问题,TLB容量限制导致。
- 缓解方法:
- 使用可变页大小 (Variable Page Sizes / Huge Pages):通过大页面使TLB覆盖更大内存范围。
- 重新设计算法/数据结构:改善程序局部性,减少跨页访问,减小工作集。
- 缓解方法: