虚拟内存的作用

概述

在系统中所有的进程之间是共享CPU和主存内存资源的。当进程数量变多时,所需要的内存资源就会相应的增加。可能会导致部分程序没有主存空间可用。此外,由于资源是共享的,那么就有可能导致某个进程不小心写了另一个进程所使用的内存,进而导致程序运行不符合正常逻辑。

操作系统提供了一种对主存的抽象的概念,叫做虚拟内存(VM)。
虚拟内存是 硬件地址翻译、主存、磁盘文件和内核软件间的完美交互,他为每个进程提供了一个大的、一致的,私有的地址空间。

物理和虚拟寻址

计算机的主存可以看做是一个由 M 个连续的字节大小的单元组成的数组。每个字节都有一个唯一的物理地址(Physical Address,PA)。第一个字节的地址为 0,接下来的地址为 1,以此类推。CPU 访问内存的最简单的方式是使用物理寻址(physical addressing)。

物理寻址

虚拟内存的作用
该图例的上下文是一条加载指令,它读取从物理地址 4 处开始的 4 字节字。CPU 在执行这条指令的时候,生成一个有效物理地址,通过内存总线,把这个物理地址传递给主存,主存取出从物理地址4处开始的 4 个字节字,然后将它返回给 CPU,CPU 将它存放在一个寄存器里。早期使用物理寻址的系统通常都比较简单,比如数字信号处理器,计算器等嵌入式设备。

虚拟寻址

虚拟内存的作用
现在处理器(CPU)采用的是一个程序虚拟寻址(virtual addressing)的寻址方式。
如上图所示:
CPU 通过程序编译生成一个虚拟地址(virtual address,VA)来访问主存,这个虚拟地址在被送到主存之前会先转换成一个物理地址。
将虚拟地址转换成物理地址的任务叫做地址翻译(address translation),地址翻译需要 CPU 硬件和操作系统之间的配合。
CPU 芯片上叫做内存管理单元(Menory Management Unit, MMU)的专用硬件,利用存放在主存中的查询表来动态翻译虚拟地址,该表的内容由操作系统管理。

地址空间

地址空间(address space)是一个非负整数 地址 的有序合集:{0,1,2,… }

地址空间
虚拟内存的作用
在一个带虚拟内存的系统中,CPU 从一个有 N= 2 的 n 次方个地址的址空间中生成虚拟地址,这个地址空间就称为虚拟地址空间:{0,1,2,3,…, N-1}。
一个系统还有一个物理地址空间,对应于系统中物理内存的 M 个字节: {0,1,2,3,…, M-1}。
一个地址空间的大小通常是由表示最大地址所需要的位数来描述的,比如,一个包含 N = 2 的 n 次方 个地址的虚拟地址空间就叫做一个 n 位地址空间,现代操作系统通常支持 32 位或者 64 位虚拟地址空间。

三大作用

  • 缓存: 将主存看做一个存储磁盘空间上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据。
  • 内存管理: 为每个进程提供了一致的地址空间,简化内存管理。
  • 内存保护: 保护了每个进程的地址空间不被其他进程破坏。

重要概念:交换(swap)充分利用内存
不是所有的进程都是常驻内存的,操作系统是常驻程序;如果所有进程都常驻内存,内存一定不够。
主存只保留活动的进程,不活动的进程要休眠。进程从内存换出到磁盘上。要活动的时候从磁盘换入到内存中。

作为缓存

概念上,虚拟内存被组织为一个由存放在磁盘上的N个连续字节大小的单元组成的数组。每字节都有一个唯一虚拟地址,作为数组的索引。磁盘上的内容被缓存在内存中。
VM系统虚拟内存分割为大小固定的块,称为虚拟页(VP),来作为磁盘和主存之间的传输单元;每个虚拟页的大小为P=2^p字节。
物理内存也被分割为物理页(PP)(也称为页帧),大小也是P字节。

虚拟页面分为三个互斥子集:

  1. 未分配的:VM系统还未分配(或创建的)页。未分配的块没有任何数据和它们相关联,因此也就不占用任何磁盘空间。

  2. 缓存的:当前已缓存在物理内存中的已分配的页。

  3. 未缓存的:未缓存在物理内存中的已分配的页。

一个VM系统是如何使用主存作为缓存的
虚拟内存的作用
特别重要:
程序编译成机器指令中地址是存在了虚拟内存中(程序员可读),CPU取址执行,取的址永远都是虚拟内存中的虚拟地址(逻辑地址),让后在通过地址翻译获取真实指令所在的物理地址。
如上图:
有8个虚拟页。虚拟页0和3未分配,因此在磁盘上还不存在。
虚拟页1、4和6被缓存在物理内存中。
页2、5和7已经被分配了,但是当前并未缓存在主存中。

页表

虚拟内存的作用
VA 是通过什么方式转成 PA 呢 ?

这些功能由软硬件联合提供,包括操作系统软件,MMU 中的地址翻译硬件和一个存放在物理内存中叫页表(page table)的数据结构,页表虚拟页映射到物理页。每次地址翻译硬件将一个虚拟地址转换成物理地址时都会读取页表。

虚拟内存必须知道一个虚拟页是否放在物理页中,如果在物理页中,也需知道与之关联的物理页的具体物理地址。如果不在物理页中,需要选择一个牺牲页,要将该虚拟页从磁盘复制到DRAM(内存)中,并将被替换的牺牲页保存到磁盘中。

这些功能是由软硬件联合提供的。VM系统要实现上面的功能需要操作系统软件、MMU(内存管理单元)中的地址翻译硬件和一个存放在物理内存中叫页表的数据结构帮助。
页表就是记录了虚拟页和物理页映射关系的一种数据结构。每次地址翻译硬件将一个虚拟地址转换为物理地址时,都需要读取页表。操作系统负责维护页表的内容,以及在磁盘与DRAM(内存)之间来回传送页。

页表
虚拟内存的作用
上图展示了一个页表的基本组织结构。
虚拟地址空间中的每个页在页表中一个固定偏移量处都有一个页表条目(PTE)。
假设一个PTE由一个有效位和一个n位地址字段组成的。
有效位表示该虚拟页当前是否缓存在主存(物理内存DRAM)中。如果设置了有效位,那么一个空地址表示这个虚拟页还未被分配。否则,这个地址就指向该虚拟页在磁盘上的起始位置。
图中展示了8个虚拟页和4个物理页。
四个虚拟页(VP1、VP2、VP7和VP4)当前被缓存在DRAM(物理内存)中。
两个页(VP0、VP5)还未被分配,
而剩下的页(VP3和VP6)已经被分配了,但还未被缓存在DRAM(物理内存)中。

页命中

页命中是指,该虚拟页面是缓存的。

VM页命中
虚拟内存的作用
如图
VP2被缓存在DRAM(物理内存)中。
当CPU想要读取包含在VP2中的虚拟页的一个字时,地址翻译硬件将虚拟地址作为索引并根据页表中的PTE2来定位其物理地址,之后通过内存总线读取它。
系统通过PTE2中标志位判定VP2是已经缓存在DRAM(物理内存)中。

缺页

习惯上,DRAM中缓存不命中称为缺页

VM缺页(之前)。对VP3中的字的引用会不命中,从而触发缺页
虚拟内存的作用
上图展示了在缺页之前的页表状态的一个示例。
CPU引用了VP3的一个字,VP3并未被缓存在DRAM中。地址翻译硬件从内存中读取PTE3,从有效位推断出VP3未被缓存,进而触发了一个缺页异常。

缺页异常调用内核中的缺页异常处理程序,该程序会选择一个牺牲页,图例中就是存放在PP3中的VP4。
如果VP4已经被修改了,那么内核就会将它复制回磁盘。
无论如何,内核都会修改VP4的页表条目PTE4,反映出VP4不在缓存在主存中这一事实。

VM缺页(之后)。
缺页处理程序选择VP4作为牺牲页,并从磁盘上用VP3的副本取代它。
在缺页处理程序重新启动导致缺页的指令后,该指令将从内存中正常地读取字,而不会再产生异常。
虚拟内存的作用

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

虚拟内存作为内存管理工具

操作系统为每个进程提供一个独立的页表,因而也就是独立的虚拟地址空间

VM为每个进程提供独立的地址空间。操作系统为系统中的每个进程都维护一个独立的页表
虚拟内存的作用
如上图所示
进程 1 的页表将 VP1 映射到 PP2, VP2 映射到 PP6。
进程 2 的页表将 VP1 映射到 PP8, VP2 映射到 PP6。
注意,多个虚拟页面可以映射到同一个物理页面上。

按需页面调度和独立的虚拟地址空间的结合,对系统内存的使用和管理造成了深远的影响。VM简化了加载和链接、代码和数据的共享,以及应用程序的内存分配。

简化链接

独立地址空间允许每个进程的内存映像使用相同的基本格式。而不管代码和数据实际存放在物理内存的何处。
例如在64位x86-64平台上,代码段总是从虚拟地址0x400000开始。数据段跟在代码段后,中间夹杂着对齐空白。栈占据用户进程地址空间的最高部分,并向下增长。这样的一致性极大地简化了链接器的设计和实现,运行链接器生成完全连接的可执行文件,这些可执行文件是独立于物理内存中代码和数据的最终位置的。

进程地址空间
虚拟内存的作用
在上图中,进程 1 的页表将 VP1 映射到 PP2, VP2 映射到 PP6。进程 2 的页表将 VP1 映射到 PP8, VP2 映射到 PP6。在这里可以看到多个虚拟页面可以映射到同一个共享的物理页面上。

简化加载

虚拟内存使得容易向内存中加载可执行文件和共享对象文件。将一组连续的虚拟页面映射到任意一个文件中的任意位置的表示法称作内存映射(memory mapping)。Linux 提供了一个 nmap 的系统调用,允许应用程序自己做内存映射。

简化共享。

独立地址空间为操作系统提供了一个管理用户进程和操作系统自身之间共享的一致机制。一般情况下,每个进程都有自己私有的代码、数据、堆栈。这些内容不与其他进程共享。在这种情况下,操作系统创建页表,将相应的虚拟页映射到不连续的物理页面。

简化内存分配

虚拟内存向用户进程提供一个简单的分配额外内存的机制。当一个用户程序要求额外的堆空间时候,操作系统分配 k 个适当的连续的虚拟内存页面,并且将他们映射到物理内存的中的 k 个任意页面,操作系统没有必要分配 k 个连续的物理内存页面。

虚拟内存作为内存保护的工具

虚拟内存大大简化了内存管理,操作系统提供独立的地址空间使得区分不同进程的私有内存变得容易,但是地址翻译机制可以使用一种自然的方式拓展到提供更好的访问控制。每次 CPU 生成一个地址时,地址翻译硬件都会读一个 PTE ,通过在 PTE 上添加一些额外的控制位来控制对一个虚拟页面内容的访问。
虚拟内存的作用
在上图中,每个 PTE 添加了三个控制位, SUP 位表示进程是否必须运行在超级管理员下就是内核模式下才能访问该页,WRITE 位控制页面的写访问, EXRC 位控制页面的执行。如果有指令违反了这些控制条件,那么 CPU 会触发一个一般保护故障,将控制传递给内核中的异常处理程序。

地址翻译

虚拟内存的作用

  1. CPU 生成一个虚拟地址(VA),并把它传送给 MMU。
  2. MMU 生成 PTE(页表) 地址,并从物理内存中请求这个 PTE(页表) 。
  3. 物理内存向 MMU 返回 PTE
  4. MMU 构造物理地址(PA),并把它发送给物理内存。
  5. 物理内存读取PA地址数据返回给CPU。

地址翻译的过程执行起来太慢了?怎么解决呢?答案你应该也猜到了,就是添加缓存。在 MMU 中包含了一个 TLB (Translation Lookaside Buffer)缓存。
虚拟内存的作用