虚拟内存管理
为何引入虚拟内存
传统内存管理方式的特点
- 对于传统的内存管理,作业必须一次性全部装入内存才可以开始运行。当作业很大时,无法全部装入内存,导致大的作业无法运行,例如一个16GB的游戏无法在8GB的内存中直接运行。因此只有少量作业能够同时运行,系统的并发度低。
- 一旦作业被调入内存,就会一直常驻在内存中,但是大部分情况下一个时间段内只要访问作业的一部分数据进程就能正常运行,因此内存中会常驻许多暂时用不到的数据,导致内存的利用率低。
引入虚拟内存
综合上述对于传统内存管理的缺点,引入虚拟内存。利用局部性(包括空间局部性和时间局部性)原理,在作业装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分驻留在外存中。在程序执行过程中,若要访问的信息不在内存中,则利用调度将对应的信息从外存调入到内存中。若内存空间不足,则操作系统将内存中暂时用不到的信息调出到外存中。这样在操作系统的管理下,在用户看来内存被扩大了(例如8GB的内存能运行16GB的游戏,实际上分配给游戏进程的内存可能只有2GB,通过操作系统的管理让该游戏能够正常运行),这就是虚拟内存。
实际上物理内存空间的大小没有改变,只是在逻辑空间上进行了扩充,这也是操作系统虚拟性的一个体现。
虚拟内存的特点
-
多次性
无需一次性将作业全部装入内存,而是运行作业被分为多次调入内存
-
对换性
在作业运行时无锡常驻内存,允许在作业允许过程中,将作业调入调出内存
-
虚拟性
从逻辑空间上扩充了内存的容量,使得用户看到的内存容量远大于实际的物理内存容量
虚拟内存的实现
虚拟内存的实现包括三种方式,分别是①请求分页存储管理②请求分段存储管理③请求段页式存储管理,与传统的非连续分配存储管理(基本分页存储管理、基本分段存储管理、基本段页式存储管理)类似,不同之处在于:①在程序执行过程中,若要访问的信息不在内存中,则有操作系统负责将所需的信息从外存调入到内存中,然后执行程序;②若内存空间不足,则操作系统会将内存中暂时用不到的信息调出到外存中。下面仅介绍请求分页存储管理,其他两种管理方式类似。
请求分页存储管理
请求分页存储管理的页表如下所示:
| 页号 | 内存块号 | 状态位 | 访问字段 | 修改位 | 外存地址 |
|---|---|---|---|---|---|
| 0 | null | 0 | 0 | 0 | X |
| 1 | b | 1 | 10 | 0 | Y |
| 2 | c | 1 | 6 | 1 | Z |
- 前两项与基本分页存储管理的页表一致,表明进程中页号对应的内存块号
- 状态位表明该页面是否已经调入内存中
- 访问字段可以记录最近该页被访问了多少次,或者是上次访问的时间,用于页面替换算法使用
- 修改位表明页面在调入后是否被修改,如果没有被修改则在被替换时可以选择不再写回外存中,而是直接被覆盖
- 外存地址表明进程对应的页面在外存中的存放位置
假设现在要访问的页面不在内存中,即状态位为0,则会产生一个缺页中断,此时操作系统会使用中断处理程序处理中断,此时缺页的进程会阻塞,放入阻塞队伍中,等到页面调度完成后再将其唤醒,放回到就绪队伍中。
如果内存中有空闲块,则会分配一个空闲块给该进程,然后将所缺页面从外存中调入到内存中的空闲块中,并修改页表;若没有空闲块,则利用页面置换算法选择一个页面进行淘汰,并修改页表,若被淘汰的块没有被修改过,则不用写回到外存中。
例如要访问进程的第0页,发现第0页没有调入内存中,且现在有内存中的空闲块a,则将第0页对应的外存块(第X块)调入到内存块a中,调入成功后修改的页表如下:
| 页号 | 内存块号 | 状态位 | 访问字段 | 修改位 | 外存地址 |
|---|---|---|---|---|---|
| 0 | a | 1 | 0 | 0 | X |
| 1 | b | 1 | 10 | 0 | Y |
| 2 | c | 1 | 6 | 1 | Z |
![]() |
地址转换过程
在请求分页管理中页可以加入快表TLB来加速查询过程,若某个页面被换出内存,则在快表中对应的表项也要删除,否则可能会访问异常。若在快表中查找到对应页号的页表项,则可以直接通过页表项中的内存块号找到对应的物理地址。整体的地址转换过程如下图所示:

假设访问快表时间为a,访问内存的时间为b,访问外存并从外存调入内存让后更新页表、快表的时间为c。
- 若在快表中找到对应表项,则访问时间为a(访问快表命中)+b(访问对应内存块);
- 若在快表中没有找到而在页表中找到对应表项,则访问时间为a(访问快表未命中)+b(访问页表命中)+c(访问对应内存块) = a + 2b;
- 若该页面不在内存中,即访问页表发现控制位为0,则访问时间为a(访问快表未命中)+b(访问页表为命中)+c(从外存中调入)+a(访问快表命中)+b(访问对应内存块)= 2a + 2b + c
页面置换算法
当要从外存中调入页面时,需要使用页面置换算法。若分配给当前程序的驻留集(也就是操作系统分配给当前程序的内存块数,一般小于程序的总页数)仍有空闲,则可直接使用分配的空闲块,若没有则要使用页面置换算法进行页面的置换。页面的换入、换出需要磁盘的I/O操作,会有较大的开销,因此一个好的页面置换算法要追求低缺页率。页面置换算法包括最佳置换算法(OPT)、先进先出置换算法(FIFO)、最近最久未使用置换算法(LRU)、时钟置换算法(CLOCK)
- 最佳置换算法(OPT)
每次置换最长时间内不会再被使用的页面或以后不在使用的页面,需要提前知道下一次要访问的是什么页面,也就是页面访问序列,但是操作系统无法提前预测,因此最佳置换算法是无法实现的。最佳置换算法有最低的缺页率,是理想化的算法。
假设页面访问序列为7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1,系统为进程分配了3个内存块,则置换过程如下:

缺页率为 9/20 = 45%
- 先进先出置换算法(FIFO)
每次置换的页面为最早进入内存的页面,可以把调入的页面排成一个队伍,每次置换队头的页面。假设页面访问序列为3 2 1 0 3 2 4 3 2 1 0 4,系统为进程分配3个内存块,则置换过程如下:

缺页率为 9/12 = 75%
利用FIFO算法可能出现Belady异常,即为进程分配的物理块数增大,但是缺页率不减反增的异常情况,例如假设此时为进程分配了4个内存块,则置换过程如下:

此时缺页率为 10/12 = 83.3%,发生了Belady异常
- 最近最久未使用置换算法(LRU)
每次置换的页面是最近最久未使用的页面,可以在每次页面被使用后记录使用时间,当需要淘汰时,淘汰时间最久的页面。假设系统为进程分配了4个内存块,页面访问序列为1 8 1 7 8 2 7 2 1 8 3 8 2 1 3 1 7 1 3 7,则置换过程如下:

此时缺页率为 6/20 = 30% ,LRU是性能最接近OPT的算法
- 时钟置换算法(CLOCK)
时钟置换算法也称为最近未用算法(NRU)。时钟置换算法会为每个页面设置一个访问位,将内存中的页面通过指针链接成一个循环队伍。当页面被访问时,置该位为1;当需要淘汰页面时,检测访问位,若为0则淘汰该页面,若为1则置该位为0并沿着指针链顺序查找下一个页面。假设第一轮所有页面访问位均为1,则一轮扫描下来后访问位均为0,此时经过第二轮扫描就可以找到访问位为0的页面,将其替换。每次扫描从上一次指针停止的位置开始。
假设页面访问序列为 1 3 4 2 5 6 3 4 7,操作系统为进程分配了5个内存块,则过程如下

页面分配策略
驻留集指请求分页存储管理中操作系统给进程分配的内存块的集合,而工作集是指在某段时间内进程访问页面的集合,一般而言,工作集的大小不会大于工作集,否则进程运行过程中可能频繁地发生页面调入调出,产生抖动/颠簸现象(页面频繁换入换出)。
考虑驻留集的大小,可以分为固定分配和可变分配,即驻留集大小在程序运行期间是否可以改变;考虑置换策略有全局置换和局部置换,局部置换即发生缺页时只能选择自己已有的物理块进行置换,全局置换即可以选择操作系统保留的空闲物理块或别的进程持有的物理块进行置换。全局置换意味着进程的拥有的物理块会变,因此不存在固定分配全局置换。
置换策略包括以下三种:
-
固定分配局部置换:操作系统为每个进程分配一定的物理块,在运行期间保持不变。若进程发生缺页,只能选择该进程在内存中的页面进行置换,例如上面举的页面置换算法都是在固定分配局部置换的前提下。这种分配策略的缺点是要实现知晓进程需要多少个物理块才比较合适。
-
可变分配全局置换:刚开始时操作系统会为进程分配一定数量的物理块。当进程发生缺页时,从空闲物理块中取出一块分配给进程,若没有空闲物理块,则从未锁定的页面中选取。只要进程发生缺页们就会获得新的物理块。被选择的页面可能是任意一个进程中的页面,因此被选择的进程拥有的物理块会减少,导致缺页率上升。
-
可变分配局部置换:刚开始是操作系统会为进程分配一定数量的物理块,进程缺页时只允许从自己在内存中的页面进行替换。如果进程频繁地发生缺页,则操作系统会考虑为该进程多分配几个物理块,反之则减少分配的物理块。该策略是根据发生缺页的频率动态地调整进程拥有的物理块个数


浙公网安备 33010602011771号