接着我们之前学的页式管理和段式管理等传统的存储管理,我们接下来讲一下虚拟存储。
概念引入
如图所示,传统的存储管理存在一次性和驻留性,浪费了大量的内存空间,使得应用的并发度很低。
问题本质就是已有的内存容量不满足应用程序对于内存的需求。
当然,升级内存是一个很好的办法,不过也不现实,毕竟应用程序的大小从几个KB到几百GB不等,你内存再大也不可能都满足,而且内存条的成本也好高。
无法向内寻求突破,这个时候就要向外看——我们把一部分的外存视为“内存”,称之为虚拟内存。
英语一般称虚拟内存Virtual Memory,而真实内存为Real memory,狭义上也就是RAM。
既然我们选定了一部分外存当做虚拟内存,那么应该把什么样的数据放到外存呢?
这个问题根据局部性原理很好解答
局部性原理(principle of locality):程序在执行过程中的一个较短时期,所执行的指令地址和指令的操作数地址,分别局限于一定区域。
- 时间局部性:一条指令的一次执行和下次执行,一个数据的一次访问和下次访问都集中在一个较短时期内;
- 空间局部性:当前指令和邻近的几条指令,当前访问的数据和邻近的数据都集中在一个较小区域内。
根据局部性原理,我们把最常用的,最近可能访问到的部分装入内存,其余部分有需要的时候调入内存。因此,虚拟内存就有了“多次性”,“对换性”,“虚拟性”的三个特点。
- 多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存。
- 对换性:无需在作业运行时一直常驻内存,而是允许在作业运行过程中,作业换入、换出。
- 虚拟性:从适辑上扩充了内存的容量,使用户看到的内存容量,远大于实际的容量。
从哲学的角度就是“立足当下,抓住主要矛盾”。本质上以CPU时间和外存空间换取内存空间,这是操作系统中的资源转换技术。
具体实现
主要有三种方式
- 请求分页存储管理
- 请求分段存储管理
- 请求段页式存储管理
请求分页存储管理
请求分页存储管理是在传统的页式管理的基础上加上了以下部分:
- 如果当前需要访问的页面不在内存中,需要请求OS从外存调入内存
- 如果内存空间不够用的时候,OS负责把暂时用不到的页面放入外存
一句话概括,就是”请进来,送出去“。而OS中专门负责这一工作的就是缺页中断机构。
缺页中断是因为当前执行的指令想要访问的目标页面未调入内存而产生的,因此属于内中断
一条指令在执行期间,可能产生多次缺页中断。(如copy A to B,即将逻辑地址A中的数据复制到逻辑地址B,而A、B属于不同的页面,则有可能产生两次中断)
第一种情况,如果访问的页面不在内存,发生缺页中断
缺页中断实际上是内中断,每次需要将缺页的进程阻塞,然后去外存调页,调页完成后唤醒缺页进程。其中,调页还要考虑内存中有没有空闲块,如果有就分配一个,没有就需要调用页面置换算法淘汰其中一个,将调入页面装入。
而想要实现上述过程又需要一些辅助信息,比如如何知道内存中有无空闲块,需要淘汰哪个页面,淘汰的页面是否需要写回外存,如何记录和利用这些信息呢?
此时,我们就可以利用原先的页表,在其基础上加入三个字段:一个是状态位,记录当前内存块是否在内存中;访问字段可记录最近被访问过几次,或记录上次访问的时间,供置换算法选择换出页面时参考;修改位记录内存块是否修改过。
对比我们的基本分页管理,请求分页新增了三步
- 请求调页(查到页表项时根据状态位进行判断)
- 页面置换(需要调入页面,但没有空闲内存块时进行)
- 需要修改请求页表中新增的表项
王道课堂上还提供了一个图具体地阐述两者的流程和区别,右侧的细节部分说的很清楚了。
这里依然要强调两点:
- 缺页中断的英文是(page fault),是一种可以恢复的中断。实际上fault中文翻译成中断/异常/故障都可以,因为学术界对概念并没有很好的统一。可以恢复是说缺页中断是可以被缺页中断机构解决,而有些中断是不可恢复的,比如除0,此时程序就会终止(abort)。
- 请求调页和页面置换涉及到换入/换出页面,如果频繁操作系统开销会很大,本质上虚拟存储的思想是通过时间来换取空间,利用虚拟存储机制来换取更大内存容量。