操作系统之虚拟内存
# 概述
为了更加有效地管理内存并减少出错,现代操作系统提供了一种对主存的抽象概念,即是虚拟内存(Virtual Memory)。虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)
理解不深刻的人会认为虚拟内存只是“使用硬盘空间来扩展内存“的技术,这是不对的。虚拟内存的重要意义是它定义了一个连续的虚拟地址空间,使得程序的编写难度降低。并且,把内存扩展到硬盘空间只是使用虚拟内存的必然结果,虚拟内存空间会存在硬盘中,并且会被内存缓存(按需),有的操作系统还会在内存不够的情况下,将某一进程的内存全部放入硬盘空间中,并在切换到该进程时再从硬盘读取(这也是为什么Windows会经常假死的原因...)。
虚拟内存主要提供了如下三个重要的能力:
- 它把主存看作为一个存储在硬盘上的虚拟地址空间的高速缓存,并且只在主存中缓存活动区域(按需缓存)。
- 它为每个进程提供了一个一致的地址空间,从而降低了程序员对内存管理的复杂性。
- 它还保护了每个进程的地址空间不会被其他进程破坏。
# 局部性原理
要想更好地理解虚拟内存技术,必须要知道计算机中著名的局部性原理。另外,局部性原理既适⽤于程序结构,也适⽤于数据结构,是⾮常重要的⼀个概念。
局部性原理是虚拟内存技术的基础,正是因为程序运⾏具有局部性原理,才可以只装⼊部分程序到内存就开始运⾏。
早在 1968 年的时候,就有⼈指出我们的程序在执⾏的时候往往呈现局部性规律,也就是说在某个较短的时间段内,程序执⾏局限于某⼀⼩部分,程序访问的存储空间也局限于某个区域。
局部性原理表现在以下两个⽅⾯:
- 时间局部性 :如果程序中的某条指令⼀旦执⾏,不久以后该指令可能再次执⾏;如果某数据被访问过,不久以后该数据可能再次被访问。产⽣时间局部性的典型原因,是由于在程序中存在着⼤量的循环操作。
- 空间局部性 :⼀旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在⼀段时间内所访问的地址,可能集中在⼀定的范围之内,这是因为指令通常是顺序存放、顺序执⾏的,数据也⼀般是以向量、数组、表等形式簇聚存储的。时间局部性是通过将近来使⽤的指令和数据保存到⾼速缓存存储器中,并使⽤⾼速缓存的层次结构实现。空间局部性通常是使⽤较⼤的⾼速缓存,并将预取机制集成到⾼速缓存控制逻辑中实现。虚拟内存技术实际上就是建⽴了 “内存⼀外存”的两级存储器的结构,利⽤局部性原理实现髙速缓存。
# 虚拟存储器
基于局部性原理,在程序装⼊时,可以将程序的⼀部分装⼊内存,⽽将其他部分留在外存,就可以启动程序执⾏。由于外存往往⽐内存⼤很多,所以我们运⾏的软件的内存⼤⼩实际上是可以⽐计算机系统实际的内存⼤⼩⼤的。在程序执⾏过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调⼊内存,然后继续执⾏程序。另⼀⽅⾯,操作系统将内存中暂时不使⽤的内容换到外存上,从⽽腾出空间存放将要调⼊内存的信息。这样,计算机好像为⽤户提供了⼀个⽐实际内存⼤的多的存储器——虚拟存储器。
实际上,我觉得虚拟内存同样是⼀种时间换空间的策略,你⽤ CPU 的计算时间,⻚的调⼊调出花费的时间,换来了⼀个虚拟的更⼤的空间来⽀持程序的运⾏。不得不感叹,程序世界⼏乎不是时间换空间就是空间换时间。
推荐阅读王道考研操作系统知识点整理 (opens new window)
# 虚拟内存的技术实现
虚拟内存的实现需要建⽴在离散分配的内存管理⽅式的基础上。 虚拟内存的实现有三种⽅式:请求分页储存管理,请求分段储存管理,请求段页式存储管理。
# 请求分页储存管理
建⽴在分⻚管理之上,为了⽀持虚拟存储器功能⽽增加了请求调⻚功能和⻚⾯置换功能。请求分⻚是⽬前最常⽤的⼀种实现虚拟存储器的⽅法。请求分⻚存储管理系统中,在作业开始运⾏之前,仅装⼊当前要执⾏的部分段即可运⾏。假如在作业运⾏的过程中发现要访问的⻚⾯不在内存,则由处理器通知操作系统按照对应的⻚⾯置换算法将相应的⻚⾯调⼊到主存,同时操作系统也可以将暂时不⽤的⻚⾯置换到外存中。
# 请求分段储存管理
建⽴在分段存储管理之上,增加了请求调段功能、分段置换功能。请求分段储存管理⽅式就如同请求分⻚储存管理⽅式⼀样,在作业开始运⾏之前,仅装⼊当前要执⾏的部分段即可运⾏;在执⾏过程中,可使⽤请求调⼊中断动态装⼊要访问但⼜不在内存的程序段;当内存空间已满,⽽⼜需要装⼊新的段时,根据置换功能适当调出某个段,以便腾出空间⽽装⼊新的段。
# 请求段页式存储管理
请求段页式管理方式只要求将作业若干页或段装入内存就可以开始运行作业,作业的其他部分别放在外存中,等待运行需要的时候才被调入内存,
请求段页式管理方式要求相对程序按逻辑意义分段后再分页,所以相对于请求页式管理方式能够方便用户使用,便于共享、保护和动态链接。进程在启动的时候采取与装入模式,则可以根据段的意义装入某些进程运行开始阶段所需要的段。
# ⻚⾯置换算法
地址映射过程中,若在⻚⾯中发现所要访问的⻚⾯不在内存中,则发⽣缺⻚中断 。
缺⻚中断 就是要访问的⻚不在主存,需要操作系统将其调⼊主存后再进⾏访问。 在这个时候,被内存映射的⽂件实际上成了⼀个分⻚交换⽂件。
当发⽣缺⻚中断时,如果当前内存中并没有空闲的⻚⾯,操作系统就必须在内存选择⼀个⻚⾯将其移出内存,以便为即将调⼊的⻚⾯让出空间。⽤来选择淘汰哪⼀⻚的规则叫做⻚⾯置换算法,我们可以把⻚⾯置换算法看成是淘汰⻚⾯的规则。
- OPT ⻚⾯置换算法(最佳⻚⾯置换算法) :最佳(Optimal, OPT)置换算法所选择的被淘汰⻚⾯将是以后永不使⽤的,或者是在最⻓时间内不再被访问的⻚⾯,这样可以保证获得最低的缺⻚率。但由于⼈们⽬前⽆法预知进程在内存下的若千⻚⾯中哪个是未来最⻓时间内不再被访问的,因⽽该算法⽆法实现。⼀般作为衡量其他置换算法的⽅法。
- FIFO(First In First Out) ⻚⾯置换算法(先进先出⻚⾯置换算法) : 总是淘汰最先进⼊内存的⻚⾯,即选择在内存中驻留时间最久的⻚⾯进⾏淘汰。
- LRU (Least Rently Used)⻚⾯置换算法(最近最久未使⽤⻚⾯置换算法) :LRU算法赋予每个⻚⾯⼀个访问字段,⽤来记录⼀个⻚⾯⾃上次被访问以来所经历的时间 T,当须淘汰⼀个⻚⾯时,选择现有⻚⾯中其 T 值最⼤的,即最近最久未使⽤的⻚⾯予以淘汰。
- LFU (Least Frequently Used)(最少使⽤⻚⾯置换算法) : 该置换算法选择在之前时期使⽤最少的⻚⾯作为淘汰⻚。