虚拟内存 & I/O & 零拷贝( 二 )

  • 第 5 步:缺页中断处理程序检查所需的虚拟地址是否合法,确认合法后系统则检查是否有空闲物理页框号 PPN 可以映射给该缺失的虚拟页面,如果没有空闲页框,则执行页面置换算法寻找一个现有的虚拟页面淘汰,如果该页面已经被修改过,则写回磁盘,更新该页面在磁盘上的副本;
  • 第 6 步:缺页中断处理程序从磁盘调入新的页面到内存,更新页表项 PTE;
  • 第 7 步:缺页中断程序返回到原先的进程,重新执行引起缺页中断的指令,CPU 将引起缺页中断的虚拟地址重新发送给 MMU,此时该虚拟地址已经有了映射的物理页框号 PPN,因此会按照前面『Page Hit』的流程走一遍,最后主存把请求的数据返回给处理器 。
  • 1.2.1 高速缓存前面在分析虚拟内存的工作原理之时,谈到页表的存储位置,为了简化处理,都是默认把主存和高速缓存放在一起,而实际上更详细的流程应该是如下的原理图:
    虚拟内存 & I/O & 零拷贝

    文章插图
    如果一台计算机同时配备了虚拟内存技术和 CPU 高速缓存,那么 MMU 每次都会优先尝试到高速缓存中进行寻址,如果缓存命中则会直接返回,只有当缓存不命中之后才去主存寻址 。
    通常来说,大多数系统都会选择利用物理内存地址去访问高速缓存,因为高速缓存相比于主存要小得多,所以使用物理寻址也不会太复杂;另外也因为高速缓存容量很小,所以系统需要尽量在多个进程之间共享数据块,而使用物理地址能够使得多进程同时在高速缓存中存储数据块以及共享来自相同虚拟内存页的数据块变得更加直观 。
    1.2.2 加速翻译&优化页表
    虚拟内存这项技术能不能真正地广泛应用到计算机中,还需要解决如下两个问题:
    • 虚拟地址到物理地址的映射过程必须要非常快,地址翻译如何加速 。
    • 虚拟地址范围的增大必然会导致页表的膨胀,形成大页表 。
    "计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决" 。虽然虚拟内存本身就已经是一个中间层了,但是中间层里的问题同样可以通过再引入一个中间层来解决 。加速地址翻译过程的方案目前是通过引入页表缓存模块 -- TLB,而大页表则是通过实现多级页表或倒排页表来解决 。
    1.2.2.1 TLB 加速
    翻译后备缓冲器(Translation Lookaside Buffer,TLB),也叫快表,是用来加速虚拟地址翻译的,因为虚拟内存的分页机制,页表一般是保存在内存中的一块固定的存储区,而 MMU 每次翻译虚拟地址的时候都需要从页表中匹配一个对应的 PTE,导致进程通过 MMU 访问指定内存数据的时候比没有分页机制的系统多了一次内存访问,一般会多耗费几十到几百个 CPU 时钟周期,性能至少下降一半,如果 PTE 碰巧缓存在 CPU L1 高速缓存中,则开销可以降低到一两个周期,但是我们不能寄希望于每次要匹配的 PTE 都刚好在 L1 中,因此需要引入加速机制,即 TLB 快表 。
    TLB 可以简单地理解成页表的高速缓存,保存了最高频被访问的页表项 PTE 。由于 TLB 一般是硬件实现的,因此速度极快,MMU 收到虚拟地址时一般会先通过硬件 TLB 并行地在页表中匹配对应的 PTE,若命中且该 PTE 的访问操作不违反保护位(比如尝试写一个只读的内存地址),则直接从 TLB 取出对应的物理页框号 PPN 返回,若不命中则会穿透到主存页表里查询,并且会在查询到最新页表项之后存入 TLB,以备下次缓存命中,如果 TLB 当前的存储空间不足则会替换掉现有的其中一个 PTE 。
    下面来具体分析一下 TLB 命中和不命中 。
    TLB 命中:
    虚拟内存 & I/O & 零拷贝

    文章插图
    • 第 1 步:CPU 产生一个虚拟地址 VA;
    • 第 2 步和第 3 步:MMU 从 TLB 中取出对应的 PTE;
    • 第 4 步:MMU 将这个虚拟地址 VA 翻译成一个真实的物理地址 PA,通过地址总线发送到高速缓存/主存中去;
    • 第 5 步:高速缓存/主存将物理地址 PA 上的数据返回给 CPU 。
    TLB 不命中:
    虚拟内存 & I/O & 零拷贝

    文章插图