TLB — 转换检测缓冲区(快表)

一句话

TLB(Translation Lookaside Buffer)是 CPU 内部的一个硬件缓存,用于加速虚拟地址到物理地址的转换,是理解进程/线程切换开销差异的关键。

为什么需要 TLB?

现代操作系统使用虚拟内存,程序访问的是虚拟地址,CPU 需要将其转换为物理地址才能访问内存。

虚拟地址 ──→ [MMU + 页表] ──→ 物理地址 ──→ 内存

页表(Page Table)存储在主内存中,每次地址转换都需要查内存 —— 这很慢(几十到几百纳秒)。而 TLB 作为 CPU 芯片上的硬件缓存,将最近使用的页表项缓存起来,查询只需约 1 个 CPU 周期(亚纳秒级)。

TLB 的原理类似 Redis

页表 = 磁盘上的数据库(慢) TLB = Redis 缓存(快) 首次访问 miss → 查页表加载到 TLB → 后续访问 hit 直接命中

TLB 的工作方式

CPU 访问虚拟地址 VA
    ↓
TLB 中是否有 VA 对应的映射?
├── ✅ TLB Hit(命中) → 直接得到物理地址 → 访问内存
└── ❌ TLB Miss(未命中) → 查页表(内存)→ 加载到 TLB → 访问内存

TLB Miss 的代价

TLB Miss 后需要遍历页表(多级页表需多次内存访问),代价是 10~100 倍于 TLB Hit。频繁的 TLB Miss 会显著降低程序性能。

TLB 与上下文切换

这是理解 进程切换慢、线程切换快 的核心原因:

进程切换 → 必须刷新 TLB

进程 A 运行中:TLB 中缓存了进程 A 的地址映射

切换到进程 B(地址空间完全不同)

TLB 中的进程 A 映射对进程 B 无效

必须刷新(Flush)/ 使所有 TLB 项失效

进程 B 开始运行 → TLB 为空 → 大量 TLB Miss → 性能下降
  • 开销:TLB 刷新 + 重新填充(称为 TLB 抖动 / TLB Thrashing
  • 缓解:现代 CPU 引入 Tagged TLB(ASID / PCID 技术),为不同进程打标签,切换时不清空 TLB

线程切换 → 无需刷新 TLB

同一进程的线程 A → 线程 B

地址空间完全相同

TLB 中的映射依然有效

无需刷新 TLB,切换代价远小于进程切换

为什么线程切换比进程切换快?

核心原因之一就是:线程切换无需刷新 TLB。进程切换不仅要切换页表、刷新 TLB,还要刷 CPU 缓存,开销远大于线程切换。

相关概念

概念关系
MMU(内存管理单元)CPU 中负责地址转换的硬件单元,TLB 是 MMU 的一部分
页表(Page Table)存储虚拟→物理映射的数据结构,存储在内存中
ASID / PCID为 TLB 项标记进程 ID,避免进程切换时清空 TLB
大页(Huge Pages)使用更大的内存页(2MB/1GB)减少页表项数量,降低 TLB Miss

大页的优化原理

传统 4KB 页:1GB 内存需要 262144 个页表项 → TLB 覆盖范围小
2MB 大页:   1GB 内存需要 512 个页表项 → TLB 覆盖范围大 → Miss 更少

这就是为什么数据库(如 MySQL)和 Java JVM 常启用大页 —— 减少 TLB Miss,提升内存访问性能

相关笔记

  • 进程线程协程 — 进程上下文切换刷新 TLB 是切换慢的重要原因
  • Linux-Inode详解 — 文件系统的元数据缓存(类似 TLB 的内存缓存思想)