跳转到内容

虚拟内存、分页与 TLB

虚拟内存给每个进程一个完整的地址空间假象。它同时提供三件事:隔离(进程互不干扰)、共享(物理页可被映射进多个进程)与超载("比物理内存更大"的错觉)。本章讲分页、多级页表、TLB、置换算法与 mmap。

CPU 发出的都是虚拟地址,MMU(内存管理单元)查页表翻译成物理地址。翻译以页(典型 4 KB)为单位:虚拟地址 = 页号 + 页内偏移,页表记录每页的物理帧号与权限位(可读/写/执行、present、user/supervisor)。

为什么是分页不是分段:分段造成外部碎片(洞无法利用),分页只有内部碎片(每页至多浪费 4 KB)——页是"分配单位与使用单位解耦"的设计。

32 位/48 位地址空间的单级页表太大(4 KB 页、48 位地址需 512 GB 表项空间——比多数内存都大)。多级页表按需建立中间层:未用的区域中间级为空。多级页表以时间换空间——翻译要多查几级(每次一次访存),但表只在有映射时存在。

x86-64 典型四级:PGD → PUD → PMD → PTE,每次访存额外 4 次查表——不能忍,这就是 TLB 存在的原因。

**TLB(translation lookaside buffer)**缓存最近的翻译结果:几十到上千条目,命中一两个周期。TLB 命中率决定访存性能——一次 TLB 未命中要付出多级页表的多次访存(可达上百周期)。

  • 大页(huge page,2 MB/1 GB):一个条目覆盖更大范围,TLB 命中率提高——数据库与大内存应用的关键优化;代价是分配与碎片管理复杂;
  • 上下文切换:切换进程要切页表,TLB 全失效(或用 ASID/PCID 标记进程避免)——上下文切换的隐性成本之一(→ 进程、线程与上下文切换)。

物理内存不足时置换谁?LRU 理想但不可精确实现(每次访存都要更新时间戳)。近似方案:

  • 时钟算法(clock/second chance):页表有引用位(硬件访问时置 1),指针扫描,引用位为 0 的换出、为 1 的清零再给一次机会——LRU 的硬件友好近似;
  • 工作集:活跃页集合,置换出工作集外的页。

缺页:访问未映射页触发缺页中断,内核把页从磁盘调入(或 COW 复制、或按需零页)。缺页的代价是毫秒级(磁盘 I/O),比 TLB 未命中贵三个数量级——thrashing(频繁缺页)是系统卡死的形态。

  • 写时复制(COW):fork 不复制物理页,父子共享只读映射,谁写谁触发缺页复制——进程创建便宜的原因(→ 进程、线程与上下文切换);
  • mmap:文件映射进地址空间,让文件访问变成内存访问——按需分页、共享映射(多进程共享文件页)、匿名映射(大块内存)。代价:缺页的间接性与 page cache 的一致性语义(fsync 仍要显式调用)。

物理内存不足时,不活跃页换出到磁盘(swap):"比物理内存更大"的错觉由此成立,代价是缺页的毫秒级惩罚。现代服务器倾向关 swap(确定性)或配少量 swap(应急);容器与虚拟机的内存超卖依赖同样的机制。

  • 分页以 4 KB 为单位翻译;选分页弃分段是为了消除外部碎片
  • 多级页表以时间换空间:表按需存在,代价是多次查表
  • TLB 命中率决定访存性能;大页提高命中率;上下文切换使 TLB 失效
  • LRU 近似用时钟算法;缺页毫秒级,thrashing 是卡死的形态
  • COW 让 fork 便宜;mmap 让文件访问变成内存访问