面试知识库
进阶

虚拟内存原理#

一句话答案#

虚拟内存让每个进程拥有独立虚拟地址空间,通过页表映射物理内存,支持按需加载和进程隔离。

核心要点

核心机制:

  • 页表:虚拟页→物理页帧的映射
  • TLB:页表缓存(快表),加速地址翻译
  • 缺页中断:访问未加载的页 → 从磁盘加载到内存

好处: 进程隔离 / 内存超分配 / 按需加载 / 内存保护

多级页表:为什么不用单级#

单级页表的浪费: 假设 32 位系统、页大小 4KB,虚拟地址空间 4GB。单级页表要为整个地址空间预留页表项:4GB / 4KB = 2^20 ≈ 100 万项,每项 4 字节就是 4MB。而且这张表必须连续且常驻,每个进程都要一份——100 个进程就是 400MB 页表,绝大部分还是从没访问过的空洞地址。

多级页表的省内存原理: 进程的地址空间是极度稀疏的(代码段、堆、栈之间隔着大片未映射的空洞)。多级页表把”一张大表”拆成”一棵树”:顶层目录常驻,只有实际用到的地址区间才逐级分配下一层中间表。没访问过的区间,对应的中间层根本不创建(顶层项留空即可)。这样一个只用了几 MB 的小进程,页表本身可能只占几页,而不是固定 4MB。

一句话:单级表为”全部可能的地址”付费,多级表只为”真正用到的地址”付费。空间换的代价是多几次访存(见下),所以才需要 TLB 兜底。

地址翻译全过程(x86-64 四级页表):

64 位虚拟地址的低 48 位有效,从高到低切成 4 段索引 + 页内偏移:

| 9位 PML4 索引 | 9位 PDPT 索引 | 9位 PD 索引 | 9位 PT 索引 | 12位 页内偏移 |
plaintext

翻译过程逐级查表,每一级表项里存的是下一级表的物理基址

  1. CR3 寄存器 → 定位顶级页表 PML4,用 PML4 索引取出 PDPT 基址
  2. PDPT(页目录指针表):用 PDPT 索引取出 PD 基址
  3. PD(页目录):用 PD 索引取出 PT 基址
  4. PT(页表):用 PT 索引取出物理页帧号,拼上 12 位偏移得到最终物理地址

关键代价: 一次 TLB miss 要走完这 4 级,即 4 次内存访问才能拿到物理地址,然后才是第 5 次访问真正的数据。这正是 TLB 的价值——TLB 缓存”虚拟页号→物理页帧”的最终结果,命中时一次都不用查页表,直接得到物理地址。所以 TLB 命中率几乎决定了内存访问的真实性能。

缺页中断的两类(minor vs major):

CPU 查页表发现”页不在内存”(present 位为 0)时触发缺页中断,但严重程度分两种:

类型含义处理开销
minor(次要)缺页物理页其实已在内存,只是当前进程页表还没建立映射内核直接把页表项指向已有物理页快,不涉及磁盘 I/O
major(主要)缺页页确实不在内存,需从磁盘(可执行文件 / swap)读入分配物理页 + 发起磁盘 I/O 读入 + 建立映射慢,有磁盘 I/O,可能毫秒级

minor 缺页的典型来源:多进程共享同一份只读代码页(如共享库)、fork 后的 COW 页首次只读访问、page cache 中已有的文件页首次被进程映射。性能排查时 major fault 持续偏高才是真问题(说明在频繁换页 / 内存不足),minor fault 高通常无害。

面试回答(2分钟版)

虚拟内存是操作系统对物理内存的抽象,让每个进程都拥有独立的连续虚拟地址空间,32 位系统是 4GB。核心机制是通过页表将虚拟地址映射到物理地址,CPU 访问内存时先查 TLB 快表缓存,命中则直接得到物理地址,未命中才去查多级页表。虚拟内存有四个主要好处:一是进程隔离,每个进程有独立的地址空间互不干扰,一个进程崩溃不影响其他进程。二是内存超分配,进程可以使用超过物理内存大小的地址空间,因为不是所有虚拟页都需要同时在物理内存中,不常用的页可以换出到磁盘 swap 分区。三是按需加载,虚拟页被访问时如果不在物理内存中会触发缺页中断,操作系统再从磁盘加载对应页面,避免了一次性加载全部程序的浪费。四是内存保护,通过页表中的权限位控制每个页面的读写执行权限。TLB 命中率对性能至关重要,使用大页(HugePages)可以减少页表项数量从而提高 TLB 命中率。虚拟内存不等于 swap,swap 只是虚拟内存的后端存储之一。

追问与易错

追问方向:

  • “为什么能使用超过物理内存的空间?”→ 虚拟内存按需加载,不常用的页可以换出到磁盘 swap 分区,需要时再换回来,物理内存只存放当前活跃的页面
  • “TLB 命中率怎么提升?”→ 使用大页(HugePages,2MB/1GB)减少页表项数量从而提高 TLB 覆盖范围、减少进程切换(TLB 刷新)、提高数据局部性
  • “32 位虚拟地址空间多大?”→ 2^32 = 4GB,其中用户空间通常 3GB(0x00000000~0xBFFFFFFF),内核空间 1GB;64 位理论 16EB 但实际用 48 位寻址即 256TB

易错点:

  • ❌ 虚拟内存就是 swap——swap 只是一种后端
  • ❌ 虚拟内存让程序变慢——TLB 命中时几乎无开销