B05 — How the MMU Translates Virtual Addresses to Physical Addresses

Key Insight: Behind every virtual address is a complex translation process. And you never perceive it.


What is an MMU

The MMU is a hardware unit on the CPU chip. It translates Virtual Addresses (VA) to Physical Addresses (PA).

Bash
MMU 在系统中的位置:

  CPU Core                     Memory Controller
       │                             │
  执行单元 ──→ VA ──→ MMU ──→ PA ──→ Cache/DRAM
                    │
              Translation:
              VA → PA

MMU 的职责:
  1. 虚拟地址到物理地址的翻译(Address Translation)
  2. 访问权限检查(读/写/执行权限)
  3. 内存保护(防止用户进程访问内核空间)
  4. TLB 缓存(加速翻译)

为什么需要 MMU:
  - 让每个进程有独立的地址空间(隔离)
  - 支持虚拟内存(内存可以比实际物理内存大)
  - 防止恶意程序访问其他进程或内核的内存

Virtual Address to Physical Address Translation

x86-64 virtual address translation:

Bash
x86-64 虚拟地址(48-bit)的结构:

  6348(符号扩展)| 4739 | 3830 | 2921 | 2012 | 110
  [31 bits]        | PML4   | PDPT   | PD     | PT     | Offset

  每个索引 = 9 bits(512 个条目)
  页大小 = 4KB(Offset = 12 bits)
  总虚拟地址空间 = 2^48 = 256TB

  线性地址解析:
  VA[47:39] → PML4 索引(512 条目)
  VA[38:30] → PDPT  索引(512 条目)
  VA[29:21] → PD    索引(512 条目)
  VA[20:12] → PT    索引(512 条目)
  VA[11:00] → 页内偏移(4KB)

Translation Process (4-Level Page Table):

Bash
VA = 0xFFFF880000001000(48-bit)的翻译过程:

Step 1:读取 CR3(页目录基址寄存器)
  CR3 = 0x100000(假设)

Step 2:PML4[VA[47:39]]
  Index = VA[47:39] = 0x1FF(511)
  PML4[511] = 0x1010000000000863
  → 这是 PDPT 的物理地址(高 52 bits 有效)
  → 权限:User + RW + Present

Step 3:PDPT[VA[38:30]]
  PDPT 物理地址 = 0x1010000000000863 & 页对齐 = 0x1010000000000000
  Index = VA[38:30] = 0x000
  PDPT[0] = 0x1020000000000867
  → 这是 PD 的物理地址

Step 4:PD[VA[29:21]]
  PD 物理地址 = 0x1020000000000000
  Index = VA[29:21] = 0x000
  PD[0] = 0x1030000000000867
  → 这是 PT 的物理地址

Step 5:PT[VA[20:12]]
  PT 物理地址 = 0x1030000000000000
  Index = VA[20:12] = 0x001
  PT[1] = 0x0000000000000863
  → 这是 4KB 页的物理基址

Step 6:生成物理地址
  PA = 0x0000000000000863 + Offset(VA[11:0] = 0x000)
  PA = 0x0000000000000863

最终:VA 0xFFFF880000001000 → PA 0x0000000000000863

Structure of a Page Table Entry (PTE)

Bash
x86-64 PTE(Page Table Entry,8 bytes):

  63  62  60  59  58  57  56  55  52  51  50  49  48  47  40  39  32
  P   RSVD   XD   RSVD   U  RW PS   G   PAT  AVL  [  Physical Address 52:48 ]

  31                                            12   11  9    8  7  6  5  4  3  2  1  0
  [      Physical Address 47:12              ] | PAT | G |D| A| PCD| PWT| U| W| P|

字段解释:
  P(Present):页是否在内存中(0=不存在,换出到磁盘)
  RW(Read/Write):读写权限
  U(User/Supervisor):用户/内核访问权限
  PS(Page Size):0=4KB, 1=2MB/1GB(大页)
  G(Global):全局页(TLB 不刷新)
  A(Accessed):访问过(用于页面换出判断)
  D(Dirty):脏页(被写过,用于写回)
  XD(Execute Disable):禁止执行(安全)
  PCD/PWT:缓存控制

TLB(Translation Lookaside Buffer)

Every MMU address translation needs to access page tables, but page tables are in DRAM, so access latency is high (~60ns). TLB is the MMU’s built-in cache, caching recently used address translations.

Bash
TLB 的工作原理:

CPU 请求 VA = 0xFFFF880000001000
  → TLB 查询(并行检查所有 TLB 条目)
    → TLB Hit!(PTE 已在 TLB 中)
      → 直接使用 TLB 中的 PA
      → 延迟 ≈ 1 cycle(~0.3ns)

如果 TLB Miss:
  → MMU 查四级页表(60ns 延迟)
  → 发现 PA,构建 PTE
  → 把 PTE 存入 TLB(缓存)
  → 返回 PA
  → 后续访问同一页,TLB Hit

TLB 的命中率(典型):
  - L1 TLB:~90%+ 命中率(128 条目)
  - L2 TLB:~95%+ 命中率(1K~2K 条目)
  - 每次 TLB Miss 约 60ns 额外延迟

Linux TLB 刷新:
  - 当页表改变时,内核需要刷新 TLB(INVLPG/INVPCID)
  - 进程切换时(CR3 改变),自动 flush TLB

Memory Overhead of Multi-Level Page Tables

Why does x86-64 use 4-level page tables instead of a single-level page table?

Bash
一级页表的问题(2^48 个条目):

假设用一级页表(48-bit 索引):
  - 需要 2^48 个条目 × 8 bytes = 2^48 × 8 = 256 TB 的页表!
  - 完全不可行

多级页表的优势:

  假设每个进程只用 128TB 虚拟地址空间中的 128MB:

  PML4:1 个条目(1 页)
  PDPT:1 个条目(1 页)
  PD:32 个条目(32 页)
  PT:32 × 128 个条目(32 × 128 × 8 = 32KB)

  总计:4 页 = 16KB(而不是 256TB!)

  稀疏虚拟地址只需要稀疏的页表项。

多级页表的内存节省:
  - 进程只需要实际使用的虚拟地址区域对应的页表
  - 不使用的 VMA 不需要页表
  - 节省了大量内存

Huge Pages (Large Pages)

To reduce TLB misses, modern CPUs support huge pages:

Bash
x86-64 支持的大页:

  2MB 大页:PDE.PS=1,页内偏移 21 bits
  1GB 大页:PDPTE.PS=1,页内偏移 30 bits

2MB 大页的 TLB 优势:
  - 一个 TLB 条目覆盖 2MB(而不是 4KB)
  - 大型数据(数据库、机器学习)用大页可以显著减少 TLB Miss

Linux 配置大页:

  # 预留 16 个 2MB 大页(总共 32MB)
  $ echo 16 > /proc/sys/vm/nr_hugepages

  # 挂载 hugepage filesystem
  $ mount -t hugetlbfs nodev /mnt/hugepages

  程序使用:
  mmap(NULL, 32MB, PROT_READ|PROT_WRITE,
       MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB, -1, 0);

  或者使用 SHM:
  shmget(IPC_PRIVATE, 32MB, SHM_HUGETLB|IPC_CREAT|0600);

Summary

  • MMU: Memory Management Unit; hardware on the CPU chip responsible for VA to PA translation
  • 4-Level Page Table: PML4 → PDPT → PD → PT; each level has 9-bit index, 512 entries
  • Virtual Address Structure: x86-64 uses 48-bit VA; split into five 9-bit indices + 12-bit offset
  • PTE Structure: 8 bytes; contains physical base address + permission bits (RW/U/P/XD/G/A/D)
  • TLB: MMU’s built-in cache; ~60ns latency without → ~0.3ns with cache hit; 90%+ hit rate
  • Multi-Level Page Tables: Sparse VMA only needs sparse page tables; saves significant memory (single level 256TB vs 4-level 16KB)
  • Huge Pages: 2MB/1GB pages reduce TLB misses; suitable for large data applications

Next (B06): How does Linux’s buddy system manage physical memory? Physical page allocation and deallocation, and why buddy system instead of simple bitmap management.


Last modified: 2024年3月1日

Author

Comments

Write a Reply or Comment

Your email address will not be published.