深入理解计算机系统(五):不完全の探究虚拟内存

深入理解计算机系统(五):不完全の探究虚拟内存

​ ​ ​ ​ ​ ​ ​ 这篇博客适合对内存管理有一定了解的读者阅读,对于内存映射、置换算法等部分内容,不在本篇博客的讨论范围之内~

​ ​ ​ ​ ​ ​ ​ 虚拟内存为每个进程提供了一个大的、一致的和私有的地址空间。从三个方面去描述虚拟内存:

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

一、物理和虚拟寻址

​ ​ ​ ​ ​ ​ ​ 我们都知道,主存实际上是由 M M M个连续的字节大小的单元组成的数组,每个字节都有一个唯一的物理地址,CPU访问内存最原始的方式是通过物理地址,这种方式也叫做物理寻址。现在,随着时代的进步,我们采用的是一种虚拟寻址的方式。

寻址对比

二、地址空间

​ ​ ​ ​ ​ ​ ​ 地址空间是一个非负整数地址的有序集合,如果这些整数是连续的,那么我们叫做线性地址空间。一个系统有一个虚拟地址空间,还有一个物理地址空间。虚拟内存的基本思想是:允许每个数据对象有多个独立的地址,其中每个地址都选自一个不同的地址空间,也就是说主存中的每字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理空间的物理地址。

​ ​ ​ ​ ​ ​ ​ 另外,CPU从一个有 N = 2 n N=2^n N=2n个地址的虚拟地址空间中生成虚拟地址,一个地址空间的大小是由标识最大地址所需要的位数来描述的,一个包含 N = 2 n N=2^n N=2n个地址的虚拟地址空间就叫做一个 n n n位地址空间。物理地址空间对应于系统之物理内存的 M M M个字节(不一定是 2 k 2^k 2k)。

​​ ​ ​ ​ ​ ​ ​ 公式:假设虚拟地址位数为 n n n,虚拟地址数为 N N N,最大可能的虚拟地址数为 2 n − 1 2^{n-1} 2n1 N = 2 n N=2^n N=2n

三、虚拟内存作为缓存的工具

​ ​ ​ ​ ​ ​ ​ 虚拟内存被组织为一个由存放在磁盘上的 N N N个连续的字节大小的单元组成的数组,每字节都有一个唯一的虚拟地址,作为到数组的索引。虚拟内存被分割为大小固定的块,用来充当磁盘和主存之间的传输单元,这个块有个名字叫做虚拟页(VP),每个虚拟页的大小为 P = 2 p P=2^p P=2p字节,物理内存也被分割为物理页(PP),大小也是 P P P字节,物理页也叫做页帧。

​​ ​ ​ ​ ​ ​ ​ 虚拟页可以分成三种状态,你也可以理解为三类,首先是没有分配的页(不占用磁盘,因为没有数据和它们关联),其次是当前已经缓存在物理内存中的已分配页,最后是没缓存的已分配页。

虚拟内存缓存
​​ ​ ​ ​ ​ ​ ​ 如图所示是一个有8个虚拟页的虚拟内存(其中 n 、 p 、 m n、p、m npm并未明确给出对应的值),VP0和VP3还没有被分配,虚拟页1、4、6被缓存,页2、5、7并未缓存。

​ ​ ​ ​ ​ ​ ​ 在正式介绍虚拟内存如何作为缓存工具之前,需要补充一点其它的知识,我们使用术语SRAM缓存来标识位于CPU和主存之间的L1、L2、L3高速缓存,用术语DRAM缓存来表示虚拟内存系统的缓存,它在主存中缓存虚拟页。

页表

​ ​ ​ ​ ​ ​ ​ 有没有思考过这样的问题:我们怎么判断一个虚拟页是否缓存在DRAM中的某个地方,如果存在那么这个虚拟页放在哪个物理页中,如果虚拟页没有缓存,那么系统需要判断存放在磁盘的哪个位置,在物理内存中选择一个牺牲页,并将虚拟页从磁盘复制到DRAM,替换牺牲页。

​ ​ ​ ​ ​ ​ ​ 上述过程是由操作系统软件、MMU中的地址翻译硬件和页表共同实现的,页表就是将虚拟页映射到物理页的一种数据结构,页表是存放在物理内存中的。页表到底是个啥样的数据结构呢,我们可以理解为是一个页表条目(PTE)的数组,每个页在页表中固定一个偏移量处都有一个PTE,换句话说,假设一个页表有10个条目,那么就有10个PTE。每个PTE都是由一个有效位和一个 n n n位地址字段组成的。

有效位:表示该虚拟也当前是否被缓存在DRAM中,如果设置了,那么地址字段表示DRAM中相应物理页的起始位置,这个物理页缓存了虚拟页。

如果没设置,那么一个空地址表示这个虚拟页还没被分配,否则这个地址就指向该虚拟也在磁盘上的起始位置。

​​ ​ ​ ​ ​ ​ ​ 下图中显示了一共有 8 个虚拟页和 4 个物理页的页表,4 个虚拟页 VP1, VP2, VP4, VP7 当前被缓存在 DRAM 中,VP0 和 VP5 还未被分配,而剩下的 VP3 和 VP6 已经被分配了,但是当前未被缓存。

img

​​ ​ ​ ​ ​ ​ ​ 在这里还可以总结一个小公式:虚拟地址大小为 n n n,页大小为 P = 2 p P=2^p P=2p,PTE数量= 2 n / P 2^n/P 2n/P

页命中

​​ ​ ​ ​ ​ ​ ​ 当 CPU 想要读取 VP2 中的虚拟内存中的一个字时,地址翻译硬件将虚拟地址作为一个索引来定位到 PTE2, 并从主存中读取它。因为 PTE2 设置了有效位,所以 VP2 是缓存在主存中的,所以地址翻译硬件使用 PTE 中的物理内存地址构造出这个字的物理地址。

img

缺页

​ ​ ​ ​ ​ ​ ​ DRAM缓存不命中就是缺页。
img

​​ ​ ​ ​ ​ ​ ​ 如上图所示,CPU 引用了 VP3 中的一个字, VP3 并未缓存在 DRAM 中。地址翻译硬件从内存中读取 PTE3, 从有效位判断出 VP3 未被缓存,并且触发了一个缺页异常。缺页异常会调用内核的缺页异常处理程序,该程序会选择一个牺牲页。如下图所示,在这个案例中就是存放在 PP3 中的 VP4。

在这里插入图片描述
​ ​ ​ ​ ​ ​ 此时如果 VP4 已经被修改了,那么内核程序会将它复制回磁盘。接下来,内核程序从磁盘赋值 VP3 到内存中的 PP3并更新 PTE3。随后返回用户进程。当异常处理程序返回时,它会重启执行导致缺页的指令,该指令会将导致缺页的虚拟地址重新发送到地址翻译硬件。如下图所示,现在 VP3 已经在主存中了,那么就是页命中了。

在这里插入图片描述
​ ​ ​ ​ ​ ​ PS:如果分配新的虚拟内存页,以上图为例,如分配VP5,那么就是在磁盘上创建空间并更新PTE5,使他指向磁盘上新创建的页面。

四、虚拟内存作为内存管理的工具

​ ​ ​ ​ ​ ​ 每个进程都有一个独立的页表,多个虚拟页面可以映射到同一个共享物理页面上。使用虚拟内存对系统中内存的使用和管理的影响深渊,尤其是简化了链接和加载、代码和数据共享,以及应用程序的内存分配。

​ ​ ​ ​ ​ ​ 下面这幅图,就展示了多个进程如何共享一个物理页面。

img

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

​​ ​ ​ ​ ​ ​ 通过阅读前面的内容,想必你已经知道了虚拟内存可以通过独立地址空间的特点,就是根据这个特定,这使得区分不同进程的私有内存变得容易。每次CPU生成地址的适合,地址翻译硬件会读一个PTE,所以通过在PTE上加点许可位(如可读可写)控制对一个虚拟页面内容的访问。

img

​ ​ ​ ​ ​ ​ 每个PTE有3个许可位,如果一条指令违反了这些许可条件,CPU会触发保护故障,将控制权递交给内核的异常处理程序。下面我们来介绍这3个许可位:

  • SUP :表示进程是否必须运行在超级用也就是内核模式下才能访问该页
  • WRITE :控制页面的写访问
  • EXRC :控制页面的执行

​ ​ ​ ​ ​ ​ 当然了,控制位不止这些比如还有写访问WRITE等等。

六、地址翻译

​ ​ ​ ​ ​ ​ 还记得我们之前说虚拟寻址中有一个步骤是通过MMU将虚拟地址转化成物理地址的过程吗,承担这个翻译功能的是MMU中的一个硬件,那么地址翻译的过程是怎样的呢?请阅读下面的内容~
img
​ ​ ​ ​ ​ ​ 上图中展示了页面命中的场景,CPU 硬件的执行步骤:

  1. 处理器 生成一个虚拟地址,并把它传送给 MMU。
  2. MMU 生成 PTE 地址,并从高速缓存/主存中请求这个 PTE 。
  3. 高速缓存/主存向 MMU 返回 PTE。
  4. MMU 构造物理地址,并把它传送给高速缓存/主存。
  5. 高速缓存/主存返回所请求的数据字给处理器。

​ ​ ​ ​ ​ ​ 页面命中是全部由硬件来处理的,既然有页面命中,那么就有页面不命中的场景。

img

​ ​ ​ ​ ​ ​ 上图展示了页面不命中的场景, CPU 硬件的执行步骤:

  1. 处理器 生成一个虚拟地址,并把它传送给 MMU。
  2. MMU 生成 PTE 地址,并从高速缓存/主存中请求这个 PTE 。
  3. 高速缓存/主存向 MMU 返回 PTE。
  4. PTE 中的有效控制位为 0 ,所以 MMU 触发了一次异常,传递 CPU 中的控制到操作系统内核中的缺页异常处理程序。
  5. 缺页处理程序确定出物理内存中的牺牲页,如果这个页面已经被修改了,则把它换出到磁盘。
  6. 缺页处理程序调入新的页面,并更新内存中的 PTE。
  7. 缺页处理程序返回原来的进程,再次执行导致缺页的指令, CPU 将引起缺页的虚拟地址重新发送给 MMU ,因为虚拟页面现在存在主存中,所以会命中,主存将请求字返回给处理器。

​ ​ ​ ​ ​ ​ 添加缓存可以解决地址翻译的过程执行起来太慢的问题。在 MMU 中包含了一个 TLB (Translation Lookaside Buffer)缓存。

img
​ ​ ​ ​ ​ ​ 我们来看看 TLB 命中的场景,

  • 第 1 步 CPU 产生一个虚拟地址
  • 第 2 和 3 步 MMU 从 TLB 中取出对应的 PTE 。
  • 第 4 步 MMU 将这个虚拟地址翻译成一个物理地址,并且将它发送到高速缓存/主存。
  • 第 5 步 高速缓存/主存将所请求的数据字返回 CPU。

​ ​ ​ ​ ​ ​ 如下图所示,当 TLB 不命中的时候, 多了步骤 3 和 4 ,MMU 必须从 L1 缓存中取出对应的 PTE , 新取出的 PTE 存放在 TLB 中,可能会覆盖一个已经存在的 PTE 。

img

参考

本系列文章主要参考了书籍《 深入理解计算机系统 》,部分插图自己绘制,此篇博客有部分插图截取自《 The Hardware / Software Interface 》课程的PPT。

《 The Hardware / Software Interface 》

《 深入理解计算机系统 》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值