虚拟内存 & I/O & 零拷贝

作者:mosun,腾讯 PCG 后台开发工程师
一、虚拟内存 1.1 虚拟内存引入
我们知道计算机由 CPU、存储器、输入/输出设备三大核心部分组成,如下:

虚拟内存 & I/O & 零拷贝

文章插图
CPU 运行速度很快,在完全理想的状态下,存储器应该要同时具备以下三种特性:
  • 速度足够快:这样 CPU 的效率才不会受限于存储器;
  • 容量足够大:容量能够存储计算机所需的全部数据;
  • 价格足够便宜:价格低廉,所有类型的计算机都能配备;
然而,出于成本考虑,当前计算机体系中,存储都是采用分层设计的,常见层次如下:
虚拟内存 & I/O & 零拷贝

文章插图
上图分别为寄存器、高速缓存、主存和磁盘,它们的速度逐级递减、成本逐级递减,在计算机中的容量逐级递增 。通常我们所说的物理内存即上文中的主存,常作为操作系统或其他正在运行中的程序的临时资料存储介质 。在嵌入式以及一些老的操作系统中,系统直接通过物理寻址方式和主存打交道 。然而,随着科技发展,遇到如下窘境:
  • 一台机器可能同时运行多台大型应用程序;
  • 每个应用程序都需要在主存存储大量临时数据;
  • 早期,单个 CPU 寻址能力 2^32,导致内存最大 4G;
主存成了计算机系统的瓶颈 。此时,科学家提出了一个概念:虚拟内存 。
以 32 位操作系统为例,虚拟内存的引入,使得操作系统可以为每个进程分配大小为 4GB 的虚拟内存空间,而实际上物理内存在需要时才会被加载,有效解决了物理内存有限空间带来的瓶颈 。在虚拟内存到物理内存转换的过程中,有很重要的一步就是进行地址翻译,下面介绍 。
1.2 地址翻译
进程在运行期间产生的内存地址都是虚拟地址,如果计算机没有引入虚拟内存这种存储器抽象技术的话,则 CPU 会把这些地址直接发送到内存地址总线上,然后访问和虚拟地址相同值的物理地址;如果使用虚拟内存技术的话,CPU 则是把这些虚拟地址通过地址总线送到内存管理单元(Memory Management Unit,简称 MMU),MMU 将虚拟地址翻译成物理地址之后再通过内存总线去访问物理内存:
虚拟内存 & I/O & 零拷贝

文章插图
虚拟地址(比如 16 位地址 8196=0010 000000000100)分为两部分:虚拟页号(Virtual Page Number,简称 VPN,这里是高 4 位部分)和偏移量(Virtual Page Offset,简称 VPO,这里是低 12 位部分),虚拟地址转换成物理地址是通过页表(page table)来实现的 。页表由多个页表项(Page Table Entry, 简称 PTE)组成,一般页表项中都会存储物理页框号、修改位、访问位、保护位和 "在/不在" 位(有效位)等信息 。
这里我们基于一个例子来分析当分析当页面命中时,计算机各个硬件是如何交互的:
虚拟内存 & I/O & 零拷贝

文章插图
  • 第 1 步:处理器生成一个虚拟地址 VA,通过总线发送到 MMU;
  • 第 2 步:MMU 通过虚拟页号得到页表项的地址 PTEA,通过内存总线从 CPU 高速缓存/主存读取这个页表项 PTE;
  • 第 3 步:CPU 高速缓存或者主存通过内存总线向 MMU 返回页表项 PTE;
  • 第 4 步:MMU 先把页表项中的物理页框号 PPN 复制到寄存器的高三位中,接着把 12 位的偏移量 VPO 复制到寄存器的末 12 位构成 15 位的物理地址,即可以把该寄存器存储的物理内存地址 PA 发送到内存总线,访问高速缓存/主存;
  • 第 5 步:CPU 高速缓存/主存返回该物理地址对应的数据给处理器 。
在 MMU 进行地址转换时,如果页表项的有效位是 0,则表示该页面并没有映射到真实的物理页框号 PPN,则会引发一个缺页中断,CPU 陷入操作系统内核,接着操作系统就会通过页面置换算法选择一个页面将其换出 (swap),以便为即将调入的新页面腾出位置,如果要换出的页面的页表项里的修改位已经被设置过,也就是被更新过,则这是一个脏页 (Dirty Page),需要写回磁盘更新该页面在磁盘上的副本,如果该页面是"干净"的,也就是没有被修改过,则直接用调入的新页面覆盖掉被换出的旧页面即可 。缺页中断的具体流程如下:
虚拟内存 & I/O & 零拷贝

文章插图