虚拟内存和地址空间是内存虚拟化的根本,很多时候会搞不懂这个概念和原理,本文会从三个W去讲述虚拟内存,即Why,What和How
最早的操作系统是直接操作物理内存的,这个时候,每个程序都是独享物理内存空间的,所以操作系统相对比较简单;
随着时代的发展,产生了多道程序和时分共享,就是要求操作系统能够处理更多的计算机程序,这样的解决方案有两个,一是让一个程序全部占用内存运行一段时间,然后停止它,并将它状态信息保存到磁盘,最后再把下一个程序装载到内存中,继续执行,这种方案太慢了,两个程序切换太慢了;
针对上述方法改进,让多个进程共同存在于物理内存中,每个进程只保存进程一部分信息到物理内存,切换的时候,依旧保存到物理内存。这样能够有很快的效率,但是又产生进程之间物理内存打架的行为,所以需要给其提供保护和隔离两个能力
操作系统对物理内存进行了一个抽象叫地址空间,地址空间是是运行的程序看到的系统中的内存。这个抽象就是内存虚拟化的关键,就好比进程是CPU的虚拟化一样,两个都是对硬件的抽象,可以类比来看
一个地址空间包括进程所有的信息,最基本的,我们谈论最多的就是代码(指令),堆(从小到大增长),栈(从大到小增长)。当我们描述地址空间时,描述的是操作系统提供给运行程序的抽象,程序不是在物理内存0~16KB的内存中,而是在加载到任意的物理地址
虚拟内存为程序提供了一个巨大的、稀疏的、私有的地址空间(一般称为虚拟地址),这些都是对程序可见的,但是CPU运行程序的时候,是需要到实际的物理内存(一般称为物理地址或实际地址)取数据,这里需要有一个机制将虚拟地址和物理地址进行转换,一般是通过分页和分段去实现,后面会具体讨论。
这里要讨论的是,多个程序,都有自己的地址空间,两个程序的地址空间的地址可能有重复,那么如何将每个进程的地址空间映射到物理内存的不同部分,这个技术叫动态重定位。
动态重定位是给每个CPU配置两个寄存器:基址寄存器和界限寄存器。程序运行时,起始地址加载到基址寄存器,程序长度加载到界限寄存器。每次指令访问内存时,CPU硬件会在把地址发送到内存总线前,自动把基址值加到进程发出的地址值上。同时,它检查程序提供的地址是否等于或大于界限寄存器里的值。如果访问的地址超过了界限,会产生错误并中止访问,就是页缺失错误
使用基址寄存器和界限寄存器是给每个进程提供私有地址空间,同时,硬件会对这两个寄存器进行保护,只有操作系统能够访问。使用这种方式的缺点是,每次访问内存都要进行比较和加法运算,比较可以做得很快,但是加法由于进位传递时间的问题,在没有使用特殊电路的情况下会显得很慢。
虚拟内存的基本思想是:每个程序拥有自己的地址空间,这个空间被分割成多个块,每一块称作一页或页面(page)。每一页有连续的地址范围。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻执行必要的映射。当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的指令。
从某个角度来讲,虚拟内存是对基址寄存器和界限寄存器的一种综合。虚拟内存使得整个地址空间可以用相对较小的单元映射到物理内存,而不是为正文段和数据段分别进行重定位