虚拟内存
程序使用虚拟地址,硬件与操作系统负责把它转换成物理位置。分页把地址分成虚拟页号和页内偏移,用映射替换页号,偏移不变。虚拟地址如 0x7ffd4a2c 是程序视图中的位置,不要求物理内存中同编号位置保存该数据。
转换有不同成本。TLB 缓存了映射时可直接取得物理帧号;未命中就要查页表;若映射允许访问但数据不驻留,还可能陷入内核处理缺页。本课用 TLB 探测 1、内存读 100、存储服务 1,000,000 周期拉开量级,便于区分转换与调页。
次缺页(minor fault)不需要从存储设备读入,例如按需清零、写时复制或复用页缓存;主缺页(major fault)需要存储服务。两者都进入内核,但成本不同。本课只模拟从存储读入的主缺页。真实延迟随缓存、设备与系统状态变化,不是固定周期数。
虚拟内存如何工作
模型用六位虚拟地址表示 64 个字,分成八页、每页八字,映射到四个物理帧,共 32 个字。P0 是代码、P1 数据、P2 堆、P7 栈;P3–P6 未映射,作为地址空间中的空洞。四个合法页足够放入四帧,虚拟地址可命名的范围比物理容量大。
读取 a[9] 时,001001 拆成高三位虚拟页号 001(P1)和低三位偏移 001(页内字 1)。va//PAGE、va%PAGE 描述位字段,在这种固定 2 的幂分页下无需通用除法。
查找顺序如下:
- TLB:本课两个全相联表项,匹配页号后取得帧号,保留偏移,读取数据。真实 TLB 可有多级、不同相联度和容量,不必都是同一种结构。
- 页表:TLB 未命中后,读取内存中该页的映射。模型条目记录 valid 和帧号;valid 在此表示是否驻留。
- 存储:映射存在、valid=0 时,本课触发主缺页。内核取空闲帧,读入整页,设置 valid,再恢复访问。帧由空闲表分配,与虚拟页号无对应要求。
- 无映射:若页位于空洞,模型无法满足访问,终止进程并显示段错误,不会去磁盘找不存在的页。
转换公式为 pa = frame * PAGE + offset。同一映射的页和帧大小一致,页内位置不变。实际硬件可支持多种页大小,包括大页,不能把本课八字当成系统通用常量。
本课计费:TLB 命中为 1+100=101;驻留页表遍历为 1+100+100=201;主缺页为 1+100+1,000,000+100=1,000,201。探测或遍历之后仍须读取数据,不能只算转换成本。
逐步演示
默认十二次 load:0 1 2 · 8 9 10 · 16 17 18 · 3 4 5,依次访问代码、数据、堆,再回代码。
a[0]:P0、offset=0。TLB 空,页表有映射但 valid=0,缺页。空闲表给的是 F2,不是 F0。P0 的八字从存储进入 F2,valid 变 1,TLB 缓存P0→F2。花 1,000,201 周期。a[1]:TLB 命中 P0→F2,pa=2×8+1=17,花 101。a[2]:同行映射命中,101。a[8]:P1 数据页未驻留,遍历后缺页,下个空闲帧为 F0。相邻虚拟页在物理上反而倒序。花 1,000,201,TLB 两项为 P0→F2、P1→F0。a[9]、a[10]:各命中,101。a[16]:P2 缺页,进入 F3。TLB 已满,按 LRU 移除最后在第 3 次 load 使用的 P0→F2。P0 数据仍留在 F2,删除的只是缓存映射。a[17]、a[18]:P2→F3 命中。- 第 10 次
a[3]:回 P0。TLB 未命中,查页表却发现 valid=1、frame=F2,没有缺页,只花 201。重新缓存 P0 映射。 - 第 11、12 次
a[4]、a[5]:各 101。
汇总为三次主缺页、八次 TLB 命中、一次无缺页遍历、十六次内存读,总计 3,001,612 周期。其中三百万用于存储,其他仅 1612:十二次 TLB 探测、四次页表读、十二次数据读。地址拆位没有另加算术成本。线性比例条的细段受最小像素限制,不表示转换与存储时间接近。
TLB 节省什么
No TLB 仍读相同十二个地址,三次缺页时刻不变,总时间仍被存储主导。差异是内存读从 16 增到 24:每次 load 都要先读页表,再读数据。
“分页使一次访问多读一次表”不等于本运行总流量翻倍,因为启用 TLB 时本来也有四次遍历。16→24 是增加 50%。TLB 主要消除已缓存映射的页表访问,不消除页数据缺失。
TLB thrash 让四页循环三遍,超过两个表项的容量。每次回访前映射已被淘汰,命中率 0,每次都遍历,共 24 次内存读,和移除 TLB 一样。四页比两项多两页,不是一个额外页。物理数据仍可留在帧中,不能把反复 TLB 缺失当反复主缺页。
tight loop 的十二次 load 都在一页,只有一次缺页、十一次 TLB 命中、十三次内存读,展示页内局部性的收益。
若 dTLB 缺失高但缺页少,应检查转换工作集、布局、访问局部性,以及适合时的大页;若主缺页高,应检查驻留集合、首次访问与文件 I/O。增加 TLB 容量也能解决某些容量问题,但不是程序通常能直接改变的硬件参数。大页增加每项覆盖范围,也有自身分配与内存成本,需结合负载判断。
边界情况
- 首次访问可能缺页,不是必然。本模型所有合法页开始都不驻留;真实映射可能已预载或继承,文件页可能在页缓存中,按需清零或写时复制常属于次缺页。存储读取才构成这里区分的主缺页,测试应区分预热前后。
- TLB 未命中不等于缺页。默认第 10 次访问 P0 时数据一直在 F2,只需 201 周期的表与数据读。
- segfault 预设第四次读
a[40],属于未映射 P5。付 TLB 探测和页表读取后终止,无磁盘服务或帧分配。后面排队的两次 load 灰显,不计成本。真实系统也可因权限失败报告段错误,信号处理由系统和程序决定。 - 分页转换保留所用页大小下的页内偏移。它不是对每个字任意重新排列。
- 四个合法页、四帧,永不因容量强迫逐出驻留页。选受害页、回写脏页等由页面置换课展示。
常见错误
- 把主缺页当普通慢内存读。本课存储服务比一次内存读高四个数量级,等待期间内核可调度其他任务。次缺页不访问存储,应单独看计数。
- 认为页表因为是“表”就免费。它本身在内存层次中,遍历可能多次依赖读取。常见 x86-64 有四级或五级分页,缓存和大页还会改变实际遍历成本。
- 认为相邻虚拟页必在相邻物理帧。P0→F2、P1→F0 已是反例。DMA 须遵守设备与 OS 映射、固定要求;有些设备需连续物理内存,scatter-gather 或 IOMMU 可支持不连续帧,普通 malloc 不保证 DMA 可用。
- 混淆已分配与已驻留。模型可有合法映射但 valid=0,虚拟大小与常驻集大小不是同一值。
- 只靠总耗时诊断 TLB。本例存储淹没转换差异,应读 TLB、minor、major 等计数。
- 认为段错误必然说明数据已损坏。它首先表示访问不被允许;未映射、写只读页、执行不可执行页都可能触发,具体原因要查映射与权限。
模型范围
真实系统常见 4 KB 基页,也可有其他基页和大页;x86-64 可有四或五级表,硬件页表遍历器负责读取。TLB 常分指令、数据并有多级,容量依核心而异。真实条目有访问权限、脏位、访问位、不可执行位,本课无权限与写时复制。
模型不包含 RAM 满后的受害选择、脏页写回、驻留抖动。周期数用来展示量级:DRAM 可能有几十至上百纳秒延迟,存储缺页还随 SSD、机械盘、排队、CPU 频率和内核工作变化,不能把一百万当实测常数。
在支持事件的平台上,可测:
perf stat -e page-faults,minor-faults,major-faults,dTLB-load-misses ./your-program
重点区分 minor 与 major,并结合 dTLB 缺失。macOS 的 vmmap、footprint 可看虚拟与驻留内存;sudo fs_usage -f pathname 可辅助观察相关文件读取,但不能把每条文件读取都等同于一次主缺页。工具和事件支持依平台而定。