前面介绍了计算机系统的各种内存管理策略,例如分页,分段等,所有这些策略都有相同的目标,就是同时将多个进程保存在内存中,以便允许多道程序。然而,这些策略都倾向于要求每个进程在执行之前应完全处于内存中。
虚拟内存技术允许执行进程不必完全处于内存。这种方案的一个主要优点就是,程序可以大于物理内存。此外,虚拟内存将内存抽象成一个巨大的、统一的存储数组,进而实现了用户看到的逻辑内存与物理内存的分离。这种技术使得程序员不再担忧内存容量的限制。
虚拟内存还允许进程轻松共享文件和实现共享内存。此外,它为创建进程提供了有效的机制。然而,虚拟内存的实现并不容易,并且使用不当还可能会大大降低性能。
说了这么多,到底什么是虚拟内存呢?
内存管理算法的实现有一个基本要求,就是执行的指令应处于物理内存中。满足这一要求的第一种方法是,将整个逻辑地址空间置于物理内存中。动态加载可以帮助缓解这种限制,但它通常需要特殊的预防措施和程序员的额外工作。
指令应处于物理内存以便执行的要求,似乎是必要的和合理的,但它也是有缺点的,因为它将程序的大小限制为物理内存的大小。事实上,通过实际程序的研究会发现,在许多情况下并不需要将整个程序置于内存中。
例如,分析以下内容:
即使在需要整个程序的情况下,也可能并不同时需要整个程序。分段能够执行只有部分处于内存的程序,可以带来许多好处:
虚拟内存将用户逻辑内存与物理内存分开。这在现有物理内存有限的情况下,为程序员提供了巨大的虚拟内存(如图 1 所示)。
因此,虚拟内存使得编程更加容易,因为程序员不再需要担心有限的物理内存空间,只需要关注所要解决的问题。
进程的虚拟地址空间就是进程如何在内存中存放的逻辑(或虚拟)视图。通常,进程从某一逻辑地址(如地址 0)开始,连续存放,如图 2 所示。
注意,在图 2 中,随着动态内存的分配,允许堆向上生长。类似地,随着子程序的 不断调用,还允许堆栈向下生长。堆与堆栈之间的巨大空白空间(或空洞)为虚拟地址的一部分,只有在堆与堆栈生长时,才需要实际的物理页。包括空白的虚拟地址空间称为稀疏地址空间。采用稀疏地址空间的优点是随着程序的执行,堆栈或堆会生长或需要加载动态链接库(或共享对象),此时可以填充这些空白。
前面讲过,物理地址可以按帧来组织,并且分配给进程的物理帧也可以不连续。这就需要内存管理单元(MMU)将逻辑页映射到内存的物理页帧。
除了将逻辑内存与物理内存分开外,虚拟内存允许文件和内存通过共享页而为多个进程所共享。这带来了以下好处: