系列文章目录
虚拟内存作用(续)
简化内存管理
进程、虚拟内存、物理内存之间的关系 简化程序的连接、简化程序的加载、简化数据之间的共享(共享库)、简化内存分配
虚拟内存机制为进程使用内存提供了极大的方便,它为进程提供了一致的、独立的地址空间。对于32bit系统来说,每个进程都是独享4GB的虚拟内存空间,即使物理内存只有几百兆的情况下,进程感觉都是在使用4G大小的内存。
如图所说,每个进程都维护独立的页表,单独完成虚拟地址翻译工作,而且多个进程之间可以共享相同的物理页面,这是动态库、共享内存的基础。
按需页面调度和独立的地址空间的结合,对系统中内存的使用和管理造成了深远的影响。特别是,VM简化了链接和加载、代码和数据共享,以及应用程序的内存分配方式。
- 简化链接。独立的地址空间允许每个进程的内存映像使用相同的基本格式,比如,Linux中的目标文件使用的ELF格式,而不管代码和数据在物理内存中存放的位置。如下图,Linux系统下每个进程都是用类似的内存格式。
- 简化加载。虚拟内存简化了将可执行文件和共享目标加载到内存的步骤。加载器通过将目标文件的代码和数据进行分页,然后构造指向这些页面的页表,剩下的就给虚拟内存系统处理了。当进程访问到目标时,VM通过缺页处理,将相应的页面加载到内存中,从而完成目标文件的加载工作。
- 简化共享。虚拟内存简化了用户进程与操作系统之间数据共享。每个进程的地址空间都会划分出相同大小的部分用来共享内核的数据和代码,进程之间也会将虚拟页指向相同的物理页的方式来实现共享各种动态库的目的,比如,每个进程都会连接的libc库。这种机制,不但大大节省了内存空间,而且简化了进程之间数据和代码的共享。
- 简化内存分配。虚拟内存为用户进程提供了一个简单的分配额外内存的机制。当进程需要额外的堆空间时,可以通过malloc函数向OS发出请求,OS会为其分配适当数量的虚拟页面,并且将它们映射到物理内存页,物理内存页不要求是连续的。
保护内存
PTE中增加权限属性保护内存 操作系统有义务控制对内存的安全访问,比如,不允许进程修改只读代码段,不允许普通进程读取或者修改内存的代码段和数据段,不允许读写其它进程的代码段和数据段。
虚拟内存系统提供了一种自然地方式来实现对于内存的保护。CPU每次通过虚拟地址访问主存时,地址翻译硬件都会读取一个PTE,可以在PTE中增加一些额外的许可位来达到控制对于虚拟页面的访问。
这个示例中,PTE中添加了三个许可位,SUP位表示进程是否必须运行在内核下才能访问该页,运行在用户模式下的进程只能访问SUP位位NO的页面。READ位和WRITE用来控制对于页面的读写访问。比如,进程j不能写VP0这个页面。
如果一条指令违反了这些许可条件,那么CPU就会触发一个一般保护故障,Linux中一般将这种故障称为“段错误(segmentation falut)”,以表示进程访问了非法的内存。
地址翻译
基本原理
虚拟地址与物理地址之间的映射规则 翻译步骤:命中、不命中
在虚拟内存系统中,地址翻译是由CPU中的MMU单元完成的,它是个纯硬件的处理过程。 下面是地址翻译过程中需要用到的一些术语。
下图展示如何MMU如何通过页表来实现地址翻译。
对照上面的属于表来讲解下页表在地址翻译过程中的作用。
- PTBR:页表基址基础寄存器,其指向当前页面的起始位置。
- n位的虚拟地址被分为两部分:VPN和VPO,VPN用于在页表中索引PTE,VPO表示虚拟页面的偏移。
- m位的物理地址同样被分为两部分:PPN和PPO,PPN表示物理页面的编号,如果虚拟页已缓存,那么PTE中就会记录该页面所对应的物理页面的PPN,PPO位数和VPO一样,表示物理页面的偏移。
- 有效位表示虚拟页面的缓存状态。
下图展示了当页命中时,CPU硬件执行的步骤:
- 第一步:处理器生成一个虚拟地址VA,并把它传送给MMU。
- 第二步:MMU生成PTEA(PTE Address),并从高速缓存/主存请求得到对应的PTE。
- 第三步:高速缓存/主存返回PTE。
- 第四步:MMU根据PTE构造物理地址PA,并把它传送给高速缓存/主存。
- 第五步:高速缓存/内存返回所请求的数据字给处理器。
页面命中完全是由MMU硬件完成的,与之不同的是,缺页需要MMU和OS共同协作完成,如下图所示。
- 第一步到第三步:和页面命中的过程中的1-3步骤相同。
- 第四步:PTE中的有效位为0,所以触发了一次缺页异常,CPU将控制权交给缺页处理程序。
- 第五步:缺页处理程序选出物理内存中的牺牲页,如果这个页面已被修改,则把它换出到磁盘。
- 第六步:缺页处理程序调入新页,并更新内存中的PTE。
- 第七步:缺页处理程序执行完毕,返回。CPU再次执行导致缺页的指令,MMU翻译地址,并获取到PTE,由于虚拟页面已经调入内存,所以这次缓存命中,CPU得到数据字。
提速---高速缓存提升翻译速度
高速缓存通过缓存PTE、页面提升性能 为了提供CPU访问主存的速度,现代CPU架构中一般会增加高速缓存这种硬件,高速缓存硬件位于CPU和主存之间,其按照行(一般为64字节)为单位缓存主存中的数据。CPU通过物理地址访问高速缓存,所以,在有高速缓存的系统中,MMU在获取PTE或者通过物理地址获取数据字时,会首先访问高速缓存。
下图展示了有高速参与的MMU地址过程。
上图展示了一个物理寻址的高速缓存如何和虚拟内存结合起来,可以看到,地址翻译发生在缓存查找之前,页表就像其他普通的物理字一样,可以缓存在高速缓存中。
更快---利用TLB
理论上,CPU每产生一个虚拟地址,MMU就需要查询一个PTE,并且虚拟地址翻译成物理地址。最糟糕的情况下,MMU需要从内存中多去一次数据,代价是几十到几百个时钟周期。如果PTE碰巧缓存在高速缓存中,那开销下降到1个或2个时钟周期。然而,为了消除这样的开销,MMU设计了一种关于页表的独立的高速缓存:翻译后备缓冲器(Translation Lookaside Buffer TLB)。
TLB是一个小的,虚拟地址寻址的缓存,其每一行都缓存着一个PTE块。下图展示了虚拟地址中用以访问TLB的组成部分。
TLBI用于TLB的组选择,TLBT用于TLB的行匹配。如果TLB有T=2^n个组,那么TLBI就是有VPN的低t位组成,而VPN剩下的为用于组成TLBT。
下图展示了当TLB命中时所包括的步骤,由于所有的操作都是硬件自动化的,所以速度相当快。
- 第一步:CPU产生一个虚拟地址。
- 第二步和第三步:MMU从TLB中取出相应的PTE。
- 第四步:MMU将这个VA翻译成PA,并且将其发送到高速缓存/主存.
- 第五步:高速缓存/主存将所请求的数据字返回给CPU。
当TLB不命中,MMU必须从高速缓存/主存中取出相应的PTE,新取出的PTE会自动的缓存到TLB中,可能会覆盖一个已经存在的条目。
更强---多级页表
多级页表压缩虚拟内存的页表大小,大大减轻了内存的压力
目前为止,我们一直假设系统只用一个单独的页表进行地址翻译。我们稍微计算一下,就会发现只有一级页表的情况下,内存是不堪重负的。假设系统为32bit,页面大小为4KB,每个PTE为4字节,那么页表的大小为4MB大小,注意,页表必须常驻内存,而且,每个进程占用4MB,这样只有页表就会将内存挤爆!
所以,页表需要压缩,办法就是使用层次结构的页表。这里通过一个实例,演示一个二级页表的组织结构。
这个二级页表的特定如下:
- 虚拟地址为32bit,虚拟页面大小为4KB,PTE大小为4字节。
- 一级页表中的每个PTE负责映射虚拟地址空间中的一个4MB的片,一级页表的大小为1K。每个一级PTE指向一个二级页表。
- 每个二级页表大小为1K,每个PTE指向一个虚拟页。
这种方法从两个方面减少了内存的负担:
- 如果一级页表中的一个PTE是空的,那么相应的二级页表就根本不需要存在,这可以节省大量的内存空间,因为对于一个典型4GB的虚拟地址空间,大部分都是未分配的。
- 只有一级页表是需要常驻内存的,VM可以按需创建、页面调入或调出二级页表,这就更进一步减轻了主存的压力;只有最经常使用的二级页表才会常驻内存。
下面展示了Core i7 CPU的页表结构,它是四级页表结构。
可以看到虚拟地址的VPN被分为了4部分,VPN1~VPN4,其中VPN1到VNP3用于索引页表,VPN4用于索引物理页面。
总结
以上就是关于虚拟内存的主要原理介绍,VM作为主存的一个抽象,为内存的管理和使用提供了极大的便利,其促使进程可以简单、高效的使用主存这种稀有的系统资源,可以说,VM是现代计算系统不可或缺一个组成部分。对于它的了解,可以帮助我们更好的理解计算机系统的运行原理,并基于此,编写出更为高效的系统软件。Enjoy It,:)!