CSAPP-9-虚拟内存

虚拟内存定义

虚拟内存(VM)是对主存的抽象概念。虚拟内存提供了三个重要的能力:1)它将主存看成一个存储在磁盘上的地址空间的高速缓存,在主存中只保护活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式它高效的使用了主存。2)它为每个进程提供一致的地址空间,从而简化内存管理。3)他保护每个进程的地址空间不被其他进程破坏。

虚拟内存是核心的,虚拟内存编辑计算机系统的所有层面,在硬件异常、汇编器、连接器、加载器、共享对象、文件、进程的设计中扮演重要角色。虚拟内存是强大的,虚拟内存给予应用程序强大的能力,可以创建和销毁内存片,将内存片映射到磁盘文件的某个部分,以及与其他进程共享内存。虚拟内存是危险的,每次应用程序引用一个变量、间接引用一个指针、或者调用一个诸如malloc这样的动态分配程序时,它会与虚拟内存发生交互,如果虚拟内存使用不当,应用将遇到复杂危险的与内存有关的错误。

这一章从两个角度来看虚拟内存,前一部分描述虚拟内存是如何工作的,后一部分是描述应用程序如何使用和管理虚拟内存

物理和虚拟寻址

计算机系统的主存被组织成一个由M个连续得字节大小的单元组成的数组,每字节都有唯一的物理地址,第一个字节地址是0,接下来的是1,再下一个为2,以此类推,给定这种简单的结构,CPU访问内存的最自然的方式是使用物理地址,这种寻址方式成为物理寻址。下图为一个物理寻址的示例:

该示例的上下文是一条加载指令,它读取从物理地址4处开始的4字节处开始的4字节字,当CPU执行这条加载指令时,会生成一个有效的物理地址,通过内存总线把它传递给主存,主存取出从物理地址4处开始的4字节字,并将它返回给CPU,CPU会将其放在一个寄存器里。早期的PC会使用物理寻址,而且诸如数字信号处理器、嵌入式微控制器以及Cray超级计算机这样的系统仍然使用这种寻址方式,但是,现代处理器使用的是一种称为虚拟寻址的寻址形式,如下图:

使用虚拟寻址,CPU通过生成一个虚拟地址(VA)来访问主存,这个虚拟地址会在被送到内存之前转换成适当的物理地址,将一个虚拟地址转为物理地址的任务叫作地址翻译,就像异常处理一样,地址翻译需要CPU硬件和操作系统之间的紧密合作,CPU上叫作内存管理单元(MMU)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由操作系统管理。

地址空间

地址空间是一个非负整数地址的有序集合。{0,1,2,…},如果地址空间中的整数是连续的,那么我们可以说它是一个线性地址空间,为了简化讨论,假设我们使用都是线性地址空间,在一个带虚拟内存的系统中,CPU从一个有N=2^n个地址中生成虚拟地址,这个地址被称为虚拟地址空间。

一个地址空间的大小是由表示最大地址所需要的位数来描述的,例如一个包含N=2^n个地址的虚拟地址空间就叫做一个n位地址空间,现代系统通常支持32位或64位虚拟地址空间。

一个系统还有一个物理地址空间,对应系统中物理内存的M个字节,{0,1,2……M-1},M不要求是2的幂,但是为了简化讨论,假设M=2^m。

地址空间的概念是很重要的,它区分了数据对象(字节)和它们的属性(地址),一旦有这种区别,这就允许每个数据对象有多个独立的地址,其中每个地址都选自一个不同的地址空间,这就是虚拟内存的基本思想。主存中每字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。

虚拟内存作为缓存的工具

概念上而言,虚拟内存被组织为一个由寻访在磁盘上的N个连续的字节大小的单元组成的数组,每字节都有一个唯一的虚拟地址,作为数组的索引,磁盘上数组的内容被缓存在主存中,和存储器层次结构中的其他缓存一样,磁盘(较低层)上的数据被分割成块,这些块作为磁盘和主存之间的传输单元,VM系统通过将虚拟内存分割,称为虚拟页(virtual page,VP)的大小固定的块来处理这个问题,每个虚拟页的大小为P=2^p字节,类似的,物理内存被分割成物理页(Physical page),大小也为P字节(物理页也被称为页帧(page frame))。

** 在任意时刻,虚拟页面的集合都分为三个不相交的子集:**

  • 未分配的:VM系统还未分配或者创建的页,未分配的块没有任何数据与他们相关联,因此也就不占用任何磁盘空间。
  • 缓存的:当前已缓存在物理内存中的已分配页。
  • 未缓存的:未缓存在物理内存中的已分配页。

下图展示了一个有8个虚拟页的小虚拟内存,虚拟页0和虚拟页3还没有被分配,因此在磁盘上还不存在,虚拟页1、4和6都被缓存在物理内存中,页2、5和7已经被分配了,但是当前并未缓存在主存中。

DRAM缓存的组织结构

SRAM缓存表示CPU和主存之间的L1,L2和L3高速缓存,并用DRAM缓存表示虚拟内存系统的缓存,它在主存中缓存虚拟页。在存储层次结构中,DRAM缓存的位置对它的组织结构有很大影响。

页表

同任何缓存一样,虚拟内存系统必须有某种方法来判定一个虚拟页是否缓存在DRAM中的某个地方,如果是,系统还必须能确定虚拟页放在哪个物理页中,如果不命中,系统必须判定这个虚拟页存放在磁盘的哪个位置,在物理内存中选择一个牺牲页,并将虚拟页从磁盘复制到DRAM中,替换掉这个牺牲页。

这些功能是软硬件联合提供的,包括操作系统软件、MMU中的地址翻译和一个存放在物理内存中的页表的数据结构,页表是将虚拟页映射到物理页,每次地址翻译硬件将一个虚拟地址转换为物理地址时,都会读取页表,操作系统负责维护页表的内容,以及在磁盘和DRAM之间来回传送页。下图展示一个页表的基本组织结构,页表就是一个页表条目(PTE)的数组,虚拟地址空间的每个页在页表中有一个固定偏移量处都有一个PTE,假设每个PTE都是由一个有效位和一个n位地址字段组成的,有效位表明该虚拟页当前是都被缓存在DRAM中,如果设置了有效位,那么地址字段就表示DRAM中想对应的物理页起始地址,这个物理页中缓存了虚拟页,如果没有设置有效位,那么一个空地址表示这个虚拟页还未被分配,否则,这个地址就指向该虚拟页在磁盘上的起始位置。

上图展示了一个具有8个虚拟页和4个物理页的系统的页表,四个虚拟页(VP1,VP2,VP7,VP4)被缓存在DRAM中,两个页(VP0和VP5)还没有被分配,剩下的页(VP3、VP6)已经被分配但是当前未被缓存,上图中,DRAM缓存是全相连的,所以任意物理页都可以包含任意虚拟页。

页命中

如果CPU要读包含在VP2中的虚拟地址中的一个字时会发生什么。VP2被缓存在DRAM中,使用地址翻译技术,地址翻译硬件将虚拟地址作为一个索引来定位PTE2,并从内存中读取它,因为设置了有效位,那么地址翻译硬件就知道VP2是缓存在内存中,所以使用PTE中的物理内存地址(该地址指向PP1中缓存页的起始位置),构造这个字的物理地址。

缺页

在虚拟内存的习惯说法中,DRAM缓存不命中称为缺页,下图展示了在缺页之前的实例页表的状态。CPU引用VP3中的一个字,VP3并没有缓存在DRAM中,地址翻译硬件从内存中读取PTE3,从有效位推断VP3未被缓存,并且触发一个缺页异常,缺页异常调用内核中缺页异常处理程序,该程序会选择一个牺牲页,在此例中就是存放在PP3中的VP4,如果VP4已经被修改了,那么内核就会将它复制回磁盘,无论哪种情况,内核会修改VP4的页表条目,反映出VP4不再缓存在主存这一事实。

接下来,内核从磁盘复制VP3到内存PP3处,更新PTE3,随后返回,当异常处理程序返回时,它会重新启动导致缺页的指令,该指令会把导致缺页的虚拟地址重新发送到地址翻译硬件,但是现在VP3已经缓存在主存中了,那么页命中也能由地址翻译硬件正常处理了。下图为缺页之后页表的状态。

页从磁盘换入DRAM和从DRAM换出磁盘,一直等待,也就是说,当有不命中发生时,才换入页面的这种策略叫作按需页面调度,当然也有别的策略但是所有现代系统都使用的是按需页面调度

分配页面

下图展示当操作系统分配一个新的虚拟内存页时对我们示例页表的影响。例如,调用malloc的结果,在这个例子中,VP5的分配过程是在磁盘上创建空间并更新PTE5,使它指向磁盘上这个新创建的页面。

局部性

其实虚拟内存的效率不像我们想象中很低,即使不命中的惩罚很大,其实虚拟内存工作的很好,主要归功于局部性。

尽管在整个运行过程中程序引用的不同页面的总数可能会超过物理内存总的大小,但是局部性原则保证了在任意时刻,程序将趋于在一个较小的活动页面集合上工作,这个集合叫作工作集或者常驻集合,在初始开销,也就是将工作集页面调度到内存中之后,接下来对这个工作集的引用将导致命中,而不会产生额外的磁盘流量。

只要我们的程序有良好的时间局部性,虚拟内存系统就能工作得相当好,但是不是所有的程序都是这样的,如果工作集的大小超过了物理内存的大小,那么程序将产生一种不幸的状态,叫作抖动,这时页面将不断换进换出,虽然虚拟内存通常是有效的,但是如果一个程序特别慢的话,那么就要考虑是不是发生了抖动。

垃圾收集

垃圾收集器是一种动态内存分配器,自动释放程序不再需要的已分配块,这些块被称为垃圾,自动回收堆存储的过程称为垃圾收集,在一个支持垃圾收集的系统中,应用显式分配堆块,但是从不显式的释放,C中调用malloc但从不调用free,反之垃圾收集器定期识别辣鸡块,并相应的调用free,将这些块放回空闲链表。(不详细看着一部分,C/C++基本不使用)

小结

虚拟内存是对主存的一个抽象,支持虚拟内存的处理器通过使用一种叫做虚拟寻址的间接形式来引用内存,处理器产生一个虚拟地址在被发送到主存之前,这个地址被翻译成一个物理地址,从虚拟地址空间到物理地址空间的地址翻译要求硬件和软件紧密合作,专门的硬件通过使用页表来翻译虚拟地址,而页表的内容是操作系统提供的。

虚拟内存有三个重要功能:一,它在主存中自动缓存最近使用的存放磁盘上的虚拟地址空间内容,虚拟内存缓存中的块叫作页,对磁盘上的页的引用会触发缺页,缺页将控制转移到操作系统中的一个缺页处理程序,缺页处理程序将页面从磁盘复制到主存缓存,如果必要将写回被驱逐的页。第二,虚拟内存简化了内存管理,进而简化了连接、在进程间共享数据,进程内存分配以及程序加载,最后,虚拟内存在每条页表中加入保护位,从而简化了内存保护。


参考文献说明:
《深入理解计算机系统》原书第三版;


关注下方我的公众号,定期分享Java优质文章,领取进阶高级架构师视频。

关注领取Java架构师免费资料


   转载规则


《CSAPP-9-虚拟内存》 coderluo 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Redis进阶你不得不了解的知识点-主从复制原理 Redis进阶你不得不了解的知识点-主从复制原理
文章内容 掌握Redis持久化RDB和AOF的原理和选型 理解Redis主从复制原理 能够配置Redis主从复制 一、Redis持久化Redis是一个内存数据库,为了保证数据的持久性,它提供了两种持久化方案: RDB 方式(默认)RDB方
2020-02-10
下一篇 
CSAPP-8-异常控制流 CSAPP-8-异常控制流
异常就是指令的控制流中的异常情况,用来响应处理器状态中的某些变化。控制流即指令的控制转移序列。所以异常控制流可以理解为处理器在正常指令运转中发生异常的控制处理逻辑。 异常的类型异常可以分为四类:中断(interript)、陷阱(trap)、
2019-12-09
  目录