虚拟内存
一、概述
虚拟内存术语:
术语 | 解释 |
---|---|
虚拟内存 | 在存储分配机制中,备用内存作为主存的一部分可被寻址。 虚拟存储大小受计算机系统寻址机制、可用的备用内存量的限制,不受主存位置实际数量的限制。 |
虚拟地址 | 虚拟内存中分配给某一位置的地址,该位置就像主存一部分一样可被访问。 |
虚拟地址空间 | 分配给进程的虚拟存储 |
地址空间 | 某进程的内存地址范围 |
实地址 | 内存中存储位置的地址 |
二、硬件和控制结构
分段、分页特点:
- 进程中所有内存访问都是逻辑地址,在运行时动态转换为物理地址。
- 一个进程可划分为许多块(页、段),执行过程中无需连续位于内存中。
进程执行的任何时候都在内存的部分称为进程的 常驻集(resident set)。
提高CPU利用率的方法有两种:
- 在内存中保留多个进程
- 进程可以比内存的全部空间还大(即操作系统自动按需将进程块装入内存)
由于内存只在内存中执行,该存储器称为 实存储器(real memory),简称实存。
但程序员/用户可感受到更大的内存,通常分配在硬盘上,称为 虚存储器(virtual memory),简称虚存。
简单分页 | 虚存分页 | 简单分段 | 虚存分段 |
---|---|---|---|
内存划分为固定大小的小块,称为页框 | 内存划分为固定大小的小块,称为页框 | 内存未划分 | 内存未划分 |
程序被编译器/内存管理系统划分为页 | 程序被编译器/内存管理系统划分为页 | 由程序员给编译器指定程序段 | 由程序员给编译器指定程序段 |
页框中有内部碎片 | 页框中有内部碎片 | 无内部碎片 | 无内部碎片 |
无外部碎片 | 无外部碎片 | 有外部碎片 | 有外部碎片 |
操作系统需为每个进程维护页表,说明页与页框间的映射关系 | 操作系统需为每个进程维护页表,说明页与页框间的映射关系 | 操作系统需为每个进程维护段表,说明段的加载地址、长度 | 操作系统需为每个进程维护段表,说明段的加载地址、长度 |
操作系统须维护一个空闲页框列表 | 操作系统须维护一个空闲页框列表 | 操作系统须维护一个内存中空闲空洞列表 | 操作系统须维护一个内存中空闲空洞列表 |
处理器使用页号、偏移量计算绝对地址 | 处理器使用页号、偏移量计算绝对地址 | 处理器使用段号、偏移量计算绝对地址 | 处理器使用段号、偏移量计算绝对地址 |
进程运行时,它的所有页必须在内存中,除非使用覆盖技术 | 进程运行时,并非所有页都须在内存页框中,仅在需要时读入 | 进程运行时,它的所有段必须在内存中,除非使用覆盖技术 | 进程运行时,并非所有段都须在内存页框中,仅在需要时读入 |
把一页读入可能需要把另 一页 写出到硬盘 | 把一段读入可能需要把另 一页/几段 写出到硬盘 |
1. 局部性 & 虚拟内存
在稳定状态,内存的几乎所有空间都被进程块占据,处理器和操作系统能直接访问到尽可能多的进程。因此,当操作系统读取一块时,它必须把另一块换出,如果一块正好在将要用到之前换出,操作系统就不得不很快将它取回,这类操作通常会导致一种称为 系统抖动(thrashing) 的情况:处理器大部分时间用于交换块而非执行指令。
这类推断基于 局部性原理(principle of locality) ,它描述了一个进程中程序和数据引用的集簇倾向,它表明虚拟内存的方案是可行的。
2. 分页
虚拟分页与简单分页一样,同样需要页表,通常每个进程哟个有一个唯一页表。由于一个进程可能只有一些页在内存中,因此每个页表项需要有一位(P)表示它对应的页是否在内存中。还需有一修改位(M)表示相应页内容从上次装入内存后到现在是否已改变;若未改变,则无需用页框内容更新该页。
页表结构
从虚拟地址到物理地址转换需要页表,虚拟地址又称逻辑地址(页号 + 偏移量)→ 物理地址(页框号 + 偏移量)。
当某个特定进程运行时,寄存器保存该进程页表的起始地址,虚拟地址页号用于检索页表、查找页框号,并与偏移量结合形成实地址。一般来说,页号域长于页框号域(n > m)
在大多数系统中,每个进程都有一个页表。每个进程可以占据大量的虚存空间。但是,采用这种方法放置页表的内存空间太大,为克服这个问题,可以将其保存在虚存中,一样服从分页管理。
一些处理器使用两级方案来组织大型页表:其中有一个页目录,其中的每项指向一个页表。因此,如果页目录的长度为X,且一个页表的最大长度为Y,则一个进程可以有XY页。
虚拟地址的前10位用于检索根页表,查找关于用户页表的页的页表项。若该页不在内存中,则发生一次缺页中断。若在内存中,则用虚拟地址中后面10位检索用户页表项页,查找该虚拟地址引用的页的页表项。
倒排页表
替换一级/多级页表的一种方法是使用 倒排页表:虚拟地址的页号部分使用一个简单的散列函数映射到散列表中。散列表中包含倒排表的指针,而倒排表中含有页表项。该页表结构使用页框号而非虚拟页号检索页表项。
倒排页表页表项:
- 页号:虚拟地址页号部分
- 进程标识符:使用该页的进程
- 控制位:一些标记:有效、访问、修改
- 链指针:若该项没有链项,则域为空,否则包含下一项的索引值(0 ~ 2m-1)
转换检测缓冲区(TLB)
为解决虚拟内粗访问可能会引起两次物理内存的访问(取相应页表项 + 取所需数据)问题,为页表项使用一个特殊的高速缓存,称为 转换检测缓冲区(Translation Lookaside Buffer, TLB)
它与 高速缓冲器(Cache) 相似,包含最近用过的页表项。给定一个虚拟地址,处理器首先检查TLB,若需要的页表项在其中(TLB命中),则检索页框号并形成实地址。若未找到需要的页表项(TLB未命中),则处理器用页号检索页表,并检查相应页表项,若“存在位”置为,该页在内存中,处理器同时更新TLB,使其包含这个新页表项;若“存在位”未置位(页未在内存中),则产生一次内存故障,称为 缺页(page fault),由操作系统装入所需页并更新页表。
用流程图表明TLB的使用:
最后,虚存机制需与高速缓存系统(Cache)进行交互,如图所示。
页尺寸
设计页尺寸需要考虑多方面的因素,其中一个因素是内部碎片。一方面,页越小,内部碎片总量越少;另一方面,页越小,每个进程需要的页数量就越多,就应设计更大的页表。因此,一次访问可能会出现两次缺页中断:① 读取所需页表部分 ② 读取进程页。
缺页率取决于分配给一个进程的页框数量,则软件策略也影响页尺寸。此外,页尺寸还与物理内存大小、程序大小有关。
3. 分段
分段允许程序员将内存视为多个地址空间/段组成,它的大小不相等,并且是动态的。通过 “段号 + 偏移量” 组成地址。
组织
虚拟分段的段表设计与简单分段类似,但与虚拟分页类似,需要引入存在位(P)、修改位(M)等控制位。
4. 段页式
分页 / 分段 特点:
- 分页:消除了外部碎片,因而能更有效的使用内存。
- 分段:具有处理不断增长的数据结构的能力,支持共享、保护能力。
为结合二者优点,有些系统配备了特殊的处理器硬件、操作系统软件同时支持它们。
段页式系统中,用户地址空间被程序员分为许多段,每个段分为许多固定大小的页,页的长度等于内存中页框大小,相关的控制位在页级别处理。
5. 保护 & 共享
分段有助于实现保护与共享机制,程序不会不经意地超出范围访问。
一种更高级的方案是采用环状保护结构,编号小的内环比编号大的外环具有更大的特权。应用程序位于高层环,实用工具、操作系统服务位于中间环,0号环为操作系统内核函数所保留,基本原理如下:
- 程序可以只访问驻留在同一个环/更低权限环中的数据。
- 程序可以调用驻留在相同/更高特权环中的数据。
三、操作系统软件
操作系统的内存管理设计取决于三个基本选择:
- 是否使用虚存技术
- 使用分页、分段、分页 + 分段
- 为各种存储管理特征采用的算法
1. 读取策略
读取策略决定某页何时取入内存
请求分页(demand paging)
- 只有当访问到某页中的一个单元时,才将其读入内存。
预先分页(prepaging)
- 并不读取缺页中断请求的页,而是一次性读取连续的页。
2. 放置策略
放置策略决定一个进程驻留在实存的什么位置
一个关注放置问题的领域是 非一致存储访问(NonUniform Memory Access, NUMA),对于NUMA系统,自动放置策略希望能把页分配到能够提供最佳性能的内存。
3. 置换策略
置换策略决定在必须读取一个新页时,应当置换内存中的哪一页
页框锁定
置换策略有一个约束条件:内存中的某些页框可能是被锁定的。当页框被锁定时,当前保存在页框中的页不能被置换。大多数操作系统内核、重要控制结构、I/O缓冲区等保存在锁定的页框中。
页面置换
基本算法:
- 最佳 OPT(Optional):置换下次访问距当前时间最长的那些页,它导致的缺页中断最少,但难以实现。
- 最近最少使用 LRU(Least Recent Used):置换内存中最长时间未被引用的页,它可通过为页加“最后一次访问”的时间戳实现,但开销较大。
- 先进先出 FIFO(First In First Out):将分配给进程的页框视为一个循环缓冲区,并按循环方式移动页,它最容易实现,但有时候页需要被反复换入/换出,产生较多的缺页中断。
时钟策略(Clock Policy):
最简单的时钟策略为每个页框关联一个称为使用位的附加位。当某页首次装入内存时,将该页框的 使用位 置于1;随后被访问时仍置为1。需要置换一页时,操作系统扫描缓冲区,查找使用位 置为0的一个页框。每当操作系统遇到一个使用位为1的页框都将其置为0。
若扫描开始时,缓冲区中所有页框使用位均为0,则把遇到的第一个页框置换;若有页框使用位均为1,则指针在缓冲区完整地循环一周,把所有使用位都置为0,并停留在最初的位置上,置换该页框中的页。
页缓冲
提高分页的性能、允许使用较简单的页面置换策略的一种方法时页缓冲。
比较有代表性的VAX VMS 方法(FIFO页面置换算法),为提高性能,不丢弃置换出的页,将其分配至两链表之一:
- 未被修改,分配至空闲页链表
- 已被修改,分配至修改页链表
分配至链表,已备后续调入使用,但它们仍位于内存中。
若进程访问该页,则可迅速返回该进程的驻留集,两链表充当高速缓存角色,减少了I/O操作数。
置换策略 & 高速缓存大小
随着内存的增大,应用的局部性特性逐渐降低,作为补偿,高速缓存的大小也相应增加。
对于较大高速缓存,虚存页置换对性能有所影响,若选择置换的页框在高速缓存中,则该高速缓存块+保存在其中的页都会失效。
4. 驻留集管理
驻留集大小
固定分配策略(fixed-allocation)
- 为一个进程在内存中分配固定数量(在进程最初加载时确定)的页框,以供执行时使用。
可变分配策略(variable-allocation)
- 允许分配给一个进程的页框在该进程的生命周期中不断地发生变化。
置换范围
局部置换策略(local replacement policy)
- 仅在产生这次缺页的进程的驻留页中选择。
全局置换策略(global replacement policy)
- 把内存中所有未被锁定的页都作为置换的候选页。
常见的三种组合方式:
-
固定分配 + 局部范围
-
可变分配 + 全局范围
-
可变分配 + 局部范围
5. 清除策略
请求式清除(demand cleaning)
- 只有当一页被选择用于置换时才被写回辅存。
- 存在风险:发生缺页中断的进程在解除阻塞之前必须等待两次页传送,可能会降低处理器的利用率。
预约式清除(demand cleaning)
- 将这些已修改的多页在需要使用它们所占据的页框之前成批写回辅存
- 存在风险:辅存传送能力有限,部分页可能在置换前又被修改。
一种较好的方法时结合页缓冲技术:只清除可用于置换的页,但去除了清除和置换操作的成对关系。
6. 加载控制
系统并发度
加载控制会影响到驻留在内存中的进程数量,这称为系统并发度。
进程挂起 - 6种情况
当系统并发度减小时,一个/多个当前驻留进程须被挂起(换出)。
- 最低优先级进程
- 缺页中断进程
- 最后一个被激活的进程
- 驻留集最小的进程
- 最大空间的进程
- 具有最大剩余执行窗口的进程
用它们所占据的页框之前成批写回辅存 - 存在风险:辅存传送能力有限,部分页可能在置换前又被修改。
一种较好的方法时结合页缓冲技术:只清除可用于置换的页,但去除了清除和置换操作的成对关系。
6. 加载控制
系统并发度
加载控制会影响到驻留在内存中的进程数量,这称为系统并发度。
进程挂起 - 6种情况
当系统并发度减小时,一个/多个当前驻留进程须被挂起(换出)。
- 最低优先级进程
- 缺页中断进程
- 最后一个被激活的进程
- 驻留集最小的进程
- 最大空间的进程
- 具有最大剩余执行窗口的进程