首页 > 编程笔记 > 通用技能 阅读:1

虚拟内存是什么(非常详细)

我们日常使用的操作系统都是支持同时运行多个程序的,从技术的角度来看,这并不是一件容易做到的事情,想要支持这一特性,需要解决很多问题,笔者在这里列举几个最典型的问题。

1) 内存地址隔离问题

对于操作系统来说,应用程序是不能直接访问真实的内存(也称为物理内存)的。如果应用程序有这样的权限,那么不同的应用程序所使用的内存地址便无法相互隔离,此时恶意或非恶意的程序都可以很容易地改写其他程序的内存数据,导致内存数据被改写的程序产生数据安全或程序崩溃等严重问题。

因此操作系统禁止了应用程序直接访问物理内存,并且给每个应用程序的进程创建一个“中间层”,每个进程都只能在其独有的“中间层”中读写数据,然后再由系统将“中间层”的数据映射到物理内存中,这样不同的进程便有自己独立的内存地址空间,可以独立运行而不互相干扰,从而实现各个程序间的内存地址隔离。

2) 内存使用效率问题

为了确保程序运行的效率,程序被装载到内存中时地址空间都是连续的,但是内存的容量是有限的,所以很可能在加载几个程序后,就没有连续的大块内存给下一个程序使用了。

这个时候,如果我们想继续执行新的程序,就只能将之前程序的数据暂时写回磁盘里,等到后面需要用到的时候再读回来。这个过程中有大量的数据被换入换出,程序的执行效率及性能自然就十分低下。

所以想要提升内存的使用效率,就需要程序可以使用非连续的内存地址,而这又与程序的运行效率相冲突。操作系统的做法是创建一个地址连续的“中间层”,程序的数据都加载在这个连续的“中间层”中,然后由系统将“中间层”的连续地址映射到非连续的物理内存地址中。

3) 地址稳定性问题

程序在运行过程中想要执行某个函数,首先需要知道这个函数在内存中的地址,如果程序是直接加载在物理内存中的,那么它很可能不是从地址 0 开始加载的,而是从中间某一个地址开始,所以函数的地址是不确定的。

解决函数地址不确定问题的方法依然是给每个进程创建一个独立的“中间层”,并且这个“中间层”的地址都是从 0 开始的。由于程序只能加载进这个“中间层”,我们就能确保程序一定是从地址 0 开始加载的,这样函数的地址不会发生改变,并能在编译时确定。

通过上文可以看到,操作系统都是通过创建一个“中间层”来解决几个典型问题的,这个“中间层”就是虚拟内存,可以说虚拟内存是现代操作系统中最重要的技术之一。

那么什么是虚拟内存呢?虚拟内存又是如何解决上文提到的各种问题的呢?我们接着往下看。

什么是虚拟内存

虚拟内存技术相当于给每个进程分配一块独占且连续的内存,只不过这个内存是虚拟的。虚拟内存的简化模型如下图所示:


图 1 虚拟内存的简化模型

从简化的模型可以看到,每个进程都独享一块唯一的虚拟内存,由内核空间和用户空间组成:
虚拟内存的大小在 32 位操作系统下是 2^32B,即 4GB;在 64 位操作系统下是 2^48B,即 16TB。之所以不是 2^64B,是因为 2^48B已经足够大了,2^64B的空间只会导致系统损耗更多的资源来维护和管理这些空间。虚拟内存到物理内存是按照页来进行管理和映射的,一页的大小为 4KB。

假设有一个 32 位操作系统,其物理内存只有 2GB,下面以这个系统的虚拟内存和物理内存的映射模型为例来帮助读者加强对虚拟内存的理解,该场景如下图所示:


图 2 32位操作系统下虚拟内存和物理内存的映射模型

4GB 大小的虚拟内存被分成 1 048 576 个大小为 4KB 的页,当虚拟内存的某一页需要写入数据时,系统便会映射一块 4KB 大小的物理内存,如果虚拟内存中的页没有写入数据,系统则不会进行映射。虚拟内存到物理内存的地址映射由计算机的内存管理单元(MMU)完成,它属于硬件而不是系统软件,所以映射速度很快。

相关文章