梳理一下操作系统内存管理方面的内容
虚拟地址
在理解虚拟内存前,我们先剖析一下虚拟地址方面的概念。
在访问者看来,主存就是一个有M个字节大小的单元组成的数组,每字节都有一个唯一的物理地址(Physical Address, PA)。 它的访问地址和数组一样,第一个地址为0,后面地址依次为1,2,3-----M-2, M-1
;这叫做线性地址空间。这种自然的访问内存的方式我们称之为物理寻址(physical addressing),早期计算机通常采用这种方式寻址。
现代操作系统通常不会直接对主存进行访存,而是通过一个虚拟地址(virtual address,VA)来访问主存,这个虚拟地址在被送到主存之前会先转换成一个物理地址。将虚拟地址转换成物理地址的任务叫做地址翻译(address translation)。
地址翻译需要 CPU 硬件和操作系统之间的配合。 CPU 芯片上叫做内存管理单元(Menory Management Unit, MMU)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由操作系统管理(后序我们会发现这个表其实就是页表)。
虚拟内存
何为虚拟内存?其实就是扩张内存,将主存作为缓存的一种访存思想,本质上就是以时间换空间。
概念上而言,虚拟内存被组织为一个由存放在磁盘上的N个连续的字节大小的单元所组成的数组。每个字节都有唯一的虚拟地址作为到数组的索引,数组的内容被缓存到主存中。这样一个单一大小的”块“叫作页,虚拟内存中称为虚拟页(Virtual Page,VP),物理内存则被分割为物理页(Physical Page,PP),称为页帧(Page Frame)。
虚拟内存在计算机中也可以找到,即一种特殊的文件,称之为交换文件或分页文件。
在任意时刻,虚拟页面都分为三个不相交的部分:
- 未分配的(Unallocated):VM 系统还未分配(或者创建)的页,未分配的页没有任何数据和它们关联,因此不占用任何内存/磁盘空间。
- 缓存的(Cached):当前已缓存在物理内存中的已分配页。
- 未缓存的(UnCached):该页已经映射到磁盘上了,但是还没缓存在物理内存中。
PS:其中未分配的VP不占用任何的实际物理空间,这点要理解。32位程序地址空间就有4G,至于64位的程序它的地址空间是一个非常大的天文数字(貌似是16777216T),而目前我们的电脑高配的也就2T磁盘,16G内存。如果64位程序每个VP都映射着实际的PP。无论如何也对应不上的。并且也完全没必要一一映射,图中可以看到,地址空间内有大量的空白。毕竟程序不可能实际使用那么大的地址空间。
页表
访存地址、虚拟内存的问题都解决了,还有一件事:如何确定主存是否缓存了需要的某个虚拟页?以及,如何定位虚拟页和物理页之间的关系呢?如果虚拟页所对应的物理页不在主存又怎么办呢?计算机采用一种软硬件联合的方式解决这一系列问题。
其思想是:计算机联合操作系统软件、先前所说的能够将虚拟地址翻译为物理地址的MMU中的地址翻译硬件,以及一个存放在物理内存中一个叫作页表(Page table)的数据结构,该数据结构能够将虚拟页映射到物理页。每次MMU将虚拟地址翻译为物理地址时都会读取页表(理由接下来会详述)。页表内容由操作系统维护。
页表就是一个页表条目(Page Table Entry,PTE)的数组,虚拟地址空间的每个页在页表中一个固定偏移量处都有一个PTE。在这里我们假设每个 PTE 是由一个有效位(Valid bit)和一个 n 位地址字段组成的。有效位表明了该虚拟页当前是否被缓存在主存中。
- 有效位为 1,则主存缓存了该虚拟页。地址字段就表示主存中相应的物理页的起始位置。
- 有效位为 0,则地址字段的null表示这个虚拟页还未被分配,否则该地址就指向该虚拟页在磁盘上的起始位置。
其基本数据结构如下:
而当MMU翻译地址时,具体可分为两种情况,无论如何都要访问页表:
- 已经缓存在主存中,就需要判断出该虚拟页存在于哪个物理页中。
- 不在主存中,那么系统必须判断虚拟页存放在磁盘的哪个位置,并且在物理主存中选择一个牺牲页,并将该虚拟页从磁盘复制到主存,替换这个牺牲页。
顺便说一句,每个进程都拥有一套属于它自己的页表,并且对于每个进程而言都独享了整个4g虚拟地址空间(32位系统)。
其实,所谓的虚拟地址空间其实是不存在的,我们用页目录和页表来表示它,在使用的时候将其转换为物理内存,这样就完成了映射。在32位的系统中,页目录最多总共有1024个页目录项,每个项有4字节,每个页表最多可以索引1024个内存页,所以页目录可以覆盖的内存空间是1024 * 1024 * 4K(页大小)=4G,刚好是进程可以拥有的最大内存空间,一个进程页目录和页表最多占用1024*4K+4K=4M,所以一个4G的内存空间只需要4M的页表就可以代替,其他的数据会在真正使用的时候才装入物理内存。
页命中、缺页
对于如上显示的两种情况,将之定义为页命中和缺页。
- 首先,当我们试图读包含在虚拟内存中的一个字时,首先利用MMU将虚拟地址定位到具体的PTE,进而可以通过内存读取它,而如果该页已经被缓存在主存中,,即可直接使用PTE的物理地址定位到该字。
- 而如果该页未被缓存,就会触发一个缺页异常。缺页异常调用内核中的异常处理程序,该程序通过适当的页面调度算法选择一个牺牲页,如果该页修改过,内核会将其复制到磁盘。而无论如何,内核都会将想要读取的页从磁盘复制到内存中,而后更新PTE。当异常处理程序返回时,它会重新执行导致缺页的指令,然后继续执行页命中的流程。
参考
- [1]:《深入理解计算机系统》