深入了解虚拟内存
现代操作系统了提供了一种对主存的抽象概念,叫做虚拟内存。它为每个进程提供了一个非常大的,一致的和私有的地址空间。这里给大家分享一些关于深入了解虚拟内存,希望对大家能有所帮助。
概述
现代操作系统了提供了一种对主存的抽象概念,叫做虚拟内存。它为每个进程提供了一个非常大的,一致的和私有的地址空间。虚拟内存提供了以下的三个关键能力:
它将主存看成是一个存储在磁盘空间上的地址空间的高速缓存,主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据。
它为内阁进程提供了一致的地址空间,简化了内存管理。
它保护了每个进程的地址空间不被其他进程破坏。
虚拟内存做为缓存的工具
从概念上来说,虚拟内存被组织成为一个由存放在磁盘上的 N 个连续的字节大小的单元组成的数组,也就是字节数组。每个字节都有一个唯一的虚拟地址作为数组的索引。磁盘上活动的数组内容被缓存在主存中。在存储器结构中,较低层次上的磁盘的数据被分割成块,这些块作为和较高层次的主存之间的传输单元。主存作为虚拟内存的缓存。
虚拟内存(VM)系统将虚拟内存分割成称为虚拟页(Virtual Page,VP)的大小固定的块,每个虚拟页的大小为 P = 2 的 p 次方 字节。同样的,物理内存被分割为物理页(Physical Page,PP),大小也为 P 字节(物理页也称作页帧(page frame))。
在任意时刻,虚拟页面的集合都分为三个不相交的子集:
未分配的,VM 系统还未分配(或者创建)的页,未分配的页没有任何数据和它们关联,因此不占用任何内存空间。
缓存的,当前已缓存在物理内存中的已分配页。
未缓存的,未缓存在物理内存中的已分配页。
虚拟内存作为内存管理的工具
简化链接。独立的地址空间允许每个进程的内存映像使用相同的基本格式,而不管代码和数据实际存放在物理内存的何处。
简化加载。虚拟内存使得容易向内存中加载可执行文件和共享对象文件。将一组连续的虚拟页面映射到任意一个文件中的任意位置的表示法称作内存映射(memory mapping)。Linux 提供了一个 nmap 的系统调用,允许应用程序自己做内存映射。
简化共享。独立地址空间为操作系统提供了一个管理用户进程和操作系统自身之间共享的一致机制。一般情况下,每个进程都有自己私有的代码、数据、堆栈。这些内容不与其他进程共享。在这种情况下,操作系统创建页表,将相应的虚拟页映射到不连续的物理页面。
简化内存分配。虚拟内存向用户进程提供一个简单的分配额外内存的机制。当一个用户程序要求额外的堆空间时候,操作系统分配 k 个适当的连续的虚拟内存页面,并且将他们映射到物理内存的中的 k 个任意页面,操作系统没有必要分配 k 个连续的物理内存页面。
地址翻译
页面命中
上图中展示了页面命中的场景,CPU 硬件的执行步骤:
处理器 生成一个虚拟地址,并把它传送给 MMU。
MMU 生成 PTE 地址,并从高速缓存/主存中请求这个 PTE 。
高速缓存/主存向 MMU 返回 PTE。
MMU 构造物理地址,并把它传送给高速缓存/主存。
高速缓存/主存返回所请求的数据字给处理器。
页面命中是全部由硬件来处理的,既然有页面命中,那么就有页面不命中的场景。
页面不命中
上图展示了页面不命中的场景, CPU 硬件的执行步骤:
处理器 生成一个虚拟地址,并把它传送给 MMU。
MMU 生成 PTE 地址,并从高速缓存/主存中请求这个 PTE 。
高速缓存/主存向 MMU 返回 PTE。
PTE 中的有效控制位为 0 ,所以 MMU 触发了一次异常,传递 CPU 中的控制到操作系统内核中的缺页异常处理程序。
缺页处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到磁盘。
缺页处理程序调入新的页面,并更新内存中的 PTE。
缺页处理程序返回原来的进程,再次执行导致缺页的指令, CPU 将引起缺页的虚拟地址重新发送给 MMU ,因为虚拟页面现在存在主存中,所以会命中,主存将请求字返回给处理器。
地址翻译的过程执行起来太慢了?怎么解决呢?答案你应该也猜到了,就是添加缓存。在 MMU 中包含了一个 TLB (Translation Lookaside Buffer)缓存。
TLB 命中
我们来看看 TLB 命中的场景,
第 1 步 CPU 产生一个虚拟地址
第 2 和 3 步 MMU 从 TLB 中取出对应的 PTE 。
第 4 步 MMU 将这个虚拟地址翻译成一个物理地址,并且将它发送到高速缓存/主存。
第 5 步 高速缓存/主存将所请求的数据字返回 CPU。
如下图所示,当 TLB 不命中的时候, 多了步骤 3 和 4 ,MMU 必须从 L1 缓存中取出对应的 PTE , 新取出的 PTE 存放在 TLB 中,可能会覆盖一个已经存在的 PTE 。
TLB 不命中