虚拟内存原理#
一句话答案#
虚拟内存让每个进程拥有独立虚拟地址空间,通过页表映射物理内存,支持按需加载和进程隔离。
核心要点
核心机制:
- 页表:虚拟页→物理页帧的映射
- 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翻译过程逐级查表,每一级表项里存的是下一级表的物理基址:
- CR3 寄存器 → 定位顶级页表 PML4,用 PML4 索引取出 PDPT 基址
- PDPT(页目录指针表):用 PDPT 索引取出 PD 基址
- PD(页目录):用 PD 索引取出 PT 基址
- 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 命中时几乎无开销