前言
由于个人对虚拟内存这块特别感兴趣,所以就直接暂且跳过其他,接下来将通过几篇文章进行详细讲解,当然其他基础内容后续在我进行相应整体学习后也会同步输出文章,比如操作系统概念、程序链接、进程管理、页面置换算法、流水线、浮点指令、内存管理、磁盘管理等内容。不管周遭的环境如何,毕竟还很菜,坚持每天让自己进步一点点,放下暂时的焦虑,不如专注于眼前的学习,跟着我一起学习操作系统吧。
虚拟存储器设计初衷
缓存从高到低,主存可作为磁盘的缓存,我们将这项技术称作为虚拟存储器,基于历史观点,构造虚拟存储器的主要出于两个目的,其一是允许云计算在多个虚拟机之间有效而安全的共享存储器,其二则是受限的主存容量对程序设计所造成的极大影响。有了虚拟存储器这样可确保每个程序只能对划分给它的那部分主存进行读写操作,而主存只需存放程序中的活跃区域。虚拟存储器实现程序地址空间到物理地址的转换,通过这种转换处理加强了各个程序地址之间的保护。若用户加载多个程序直接到主存,很显然最终可能会超过主存的容量,如此一来,将程序进行卸载或转入成为程序员不可推卸的责任,加重了程序员的负担,那么怎样才能将程序员从这种情况中解放出来呢?现代操作系统将主存提供了一种对主存的抽象概念,叫作虚拟内存,虚拟内存是硬件异常,硬件地址翻译、主存、磁盘文件和内核软件的完美交互,虚拟内存主要提供了三种能力:【1】将主存看成是一个存储在磁盘上的地址空间的高速缓存(作为缓存)【2】为每个进程提供一致的地址空间,从而简化内存管理(作为内存管理)【3】防止每个进程的地址空间被其他进程所破坏(作为内存保护)。
映射原理:CPU生成一个虚拟地址(Virtual Address简称VA)来访问主存,但是在此之前需要将虚拟地址转换为物理地址,这个过程称作为地址转换或地址映射或地址翻译,为进行此操作需要CPU硬件和操作系统合作,通过内存管理单元(Memory Management Unit)上的地址翻译硬件,利用存储在主存上的查询表来翻译虚拟地址,该表的内容由操作系统管理。
页表地址翻译
虚拟内存系统将虚拟内存划分为固定大小的块,这个块我们称作为虚拟页(Virtual Page简称VP),同理将物理内存划分为物理页(Physical Page简称PP),也叫页帧(Page Frame)。这里我们需要明白虚拟地址、虚拟页、物理地址、物理页这四者之间的联系。如下图,通过CPU产生虚拟地址访问主存的物理地址,而虚拟存储器和物理存储器分别被划分成虚拟页和物理页,所以结果变成将虚拟页映射到物理页。
在虚拟存储器中,地址被划分为虚页号(Virtual Page Number简称VPN)和虚拟页页偏移(Virtual Page Offset简称VPO),如下为从虚拟页到物理页号(Physical Page Number简称PPN)的转换。物理页构成物理地址的高位部分,而页偏移保持不变,构成物理地址的低位部分。页偏移的位数决定了页的大小,当然,虚拟页地址可寻址的页数与物理地址可寻址的页数可以不同。
例如具有32位逻辑地址的操作系统,如果每个页面的大小为4KB,由于上述物理页号有18位,那么存储器物理页数为2^18,因此,逻辑上最多可以支持1GB的主存,但是实际上虚拟地址空间可支持4GB,如此为解决主存的受限控制。和缓存机制一样,我们必须有某种机制来判断一个虚拟页是否缓存在DRAM中某一个地方,如果是,那么系统必须确定这个虚拟页存放在哪个物理页中,如果未命中,系统必须确定这个虚拟页存储在磁盘的哪个位置,然后在物理内存中选择一个牺牲页,并将虚拟页从磁盘复制到DRAM中,然后替换这个牺牲页。此功能由软硬件共同提供,包括操作系统软件、MMU中的地址翻译硬件、存放在物理内存中的页表(Page Table简称PT)所决定,上述将虚拟页映射到物理页就是通过查询页表实现,一个页表是由页表条目(Page Table Entry简称PTE)的数组组成。如下为页表简要信息版本:
我们看到上述每个PTE是由一个有效位和一个包含物理页号或磁盘地址组成,有效位标识虚拟页是否被缓存在主存中, 若为1则说明该虚拟页已被缓存在主存中,若为0分为两种情况,可能是虚拟内存未创建虚拟页,也有可能是已创建虚拟页但还未缓存到主存,所以虚拟页集合由3个子集组成:【1】虚拟内存系统还未分配或未创建、【2】已缓存在物理内存中的已分配页、【3】未缓存在物理内存中的已分配页。页表在主存中的位置由硬件决定,硬件包含一个指向页表首地址的页表基址寄存器(Page Table Base Register简称PTBR)。简要翻译过程如下图所示
我们进一步得出MMU将虚拟地址映射为物理地址的详细过程:CPU产生虚拟地址,然后从虚拟地址中得到虚拟页号,接下来通过将虚拟页号作为索引去查找页表,通过得到对应PTE上的有效位来判断当前虚拟页是否在主存中,若命中则将对应PTE上的物理页号和虚拟地址中的虚拟页偏移进行串联从而构造出主存中的物理地址,否则未命中(专业名词称为“缺页”),此时MMU将引发缺页异常,从CPU传递到操作系统内核处理缺页异常处理程序,此时将选择一个牺牲页并将对应所缺虚拟页调入并更新页表上的PTE,缺页处理程序再次返回到原来的进程,再次执行缺页指令,CPU重新将虚拟地址发给MMU,此时虚拟页已存在物理内存中,所以命中,最终将请求的字返回给处理器。详细过程如下:
进程的地址空间以及它在主存中可以访问的所有数据,都由驻存在主存中的页表所定义,操作系统只是简单的加载页表寄存器用来指向它所想激活的进程页表,而不是保存整个页表。由于不同进程使用相同的虚拟地址,因此每个进程有各自的页表,操作系统负责分配物理主存和更新页表,因为我们通过将页表分离来保护进程,进而保证不同进程的虚拟地址空间不会发生任何冲突。上述我们已经得知,一个32位逻辑地址空间的系统,每个页面的大小为4KB(2^12),那么页表将包含(2^32/2^12)= 大约100万个页表条目即PTE,假设每个条目占4个字节,那么每个进程的页表将占用(4*(2^32/2^12))= 4MB的内存。
TLB加快地址翻译
我们知道每次CPU产生一个虚拟地址就必须通过MMU去查询页表从而得到PTE,若我们将页表放在寄存器中,虽然加速了查询但限制了页表的大小,实际上页表是放在主存上,虽然页表可以很大,但是访问会很慢,因为程序访存至少需要两次:第一次访存获取物理地址、第二次访存获得数据。提高访问性能的关键在于依靠页表的访问局部性,当一个转换的虚拟页号被使用时,它可能在不久的将来会被再次用到,因为对该页中字的引用同时具有时间局部性和空间局部性。为了消除这样的开销,在MMU中包括一个关于PTE的小的缓存,称为翻译后备缓冲器(Translation-Lookasice Buffer简称TLB),有些书中被称为快表,所以TLB存在的目的是:用于记录最近使用地址的映射信息的高速缓存,也就是缓存虚拟页到物理页(页帧)的映射(翻译),从而可以避免每次都访问页表,所以TLB的本质是缓存页表。TLB是一个小的、虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块,TLB大概包含64-1024个PTE。如下图所示
从虚拟页号中提取出用于组选择和行匹配的索引和标记字段,如果TLB有 T= 2^t个组,那么TLB索引(TLBI)是由VPN的t个最低位组成,而TLB标记(TLBT)是由VPN中剩余的位组成。上述我们了解到虚拟地址由虚拟页号和虚拟页偏移量组成,在这里再进行补充,虚拟地址由虚拟页号(VPN)、虚拟页偏移量(VPO)、TLBI(TLB索引)、TLBT(TLB标记)组成,对于物理地址由物理页号(PPN)、物理页偏移量(PPO)、缓冲块内的字节偏移量(CO)、高速缓存索引(CI)、高速缓存标记(CT)组成。那么利用TLB加快地址翻译的整个大概过程是怎样的呢?CPU产生一个虚拟地址,MMU从虚拟地址中提取出虚拟页号,然后从TLB中根据虚拟页号取出相应的PTE,进而通过物理页号和虚拟页偏移构造出物理地址,将其发送到高速缓存/主存,高速缓存/主存将返回的数据返回给CPU(现代操作系统都已有SRAM的高速缓存(一级缓存、二级缓存、三级缓存),因为地址翻译硬件发生在访问高速缓存之前,所以我们到底是通过虚拟地址还是物理地址访问高速缓存呢?大多数都是选择物理地址访问高速缓存)。整个过程如下图:
总结
本节属于小试牛刀,只是从整体上去分析虚拟内存地址翻译原理,里面仍涉及太多细节,比如页面交换策略、页表和TLB所包含详细内容,多级页表、TLB查找PTE、TLB缺失等,下一节我们分析TLB缺失和页面交换具体原理。