虚拟地址转换[五] - paging structure caches
回顾上文TLB entry和PTE entry的组成,你有没有意识到,在多级页表系统中,TLB其实只是最后一级PTE的缓存(对于large pages的TLB则最后一级是PDE或者PDPTE,本文以下的讨论都是针对非large page的情况),这和在单级页表中是一样的。
多级页表的查找是一个串行的,链式的过程。试想一下,访问在虚拟地址空间里连续的两个pages(比如虚拟地址分别为0x0000123456789000和0x000012345678A000),而这两个pages的PGD entry, PUD entry, PMD entry都是一样的,只有PTE不一样。
难道在通过N次内存访问查找到第一个page的物理页面号后,在访问相邻的(虚拟地址层面)第二个page时还需要再老老实实,一步一步的从PGD往下找?这对于在追求性能方面可谓无所不用其极的现代处理器来说,是不可接受的。
既然PTE都可以被缓存,那前面几级的PMD, PUD, PGD也应该可以被缓存吗?是的,以intel的x86-64架构为例,它支持PDE cache(使用64位虚拟地址的bits 47:21作为index/tag,对应linux里的PMD entry),PDPTE cache(使用64位虚拟地址的bits 47:30作为index/tag,对应linux里的PUD entry),PML4 cache(使用64位虚拟地址的bits 47:39作为index/tag,对应linux里的PGD entry)。
除了最后一级页表PTE的entry是直接指向page外,其他级的页表的entry都是指向下一级页表首地址的,因此这些级的页表被称为paging structure,所以PDE cache,PDPTE cache和PML4 cache被统称为paging structure caches。在ARM中,这些caches被称为table walk caches(名字应该是来自MMU里的table walk unit)。
如果发生TLB miss,相当于PTE cache中没找到,那就
- 从
PDE cache中找,如果找到了则获得对应PT页表的首地址,可继续在PT中索引到PTE,需要1次内存访问; - 没找到再从
PDPTE cache中找,如果找到了则获得对应PD页表的首地址,然后在PD->PT中索引,需要2次内存访问; - 还没找到再从
PML4 cache中找,如果找到了则获得对应PDPT页表的首地址,然后在PDPT->PD->PT中索引,需要3次内存访问; PML4 cache中也没有的话,那只能去DRAM里按照PML4->PDPT->PD->PT找了,需要4次内存访问。
可见,从TLB到DRAM,越往后,所需要的内存访问次数越多。
同TLB一样,paging structure caches需要使用专用的cache来实现,对这些cache的支持与否取决于不同处理器的具体设计。以intel的Haswell为例(i7-4770)为例,其PDE cache含有32个entries。
这篇文章曾讲到TLB中可能含有PCID/ASID来分别不同的进程,同样的,paging structure caches也是使用虚拟地址的位域子集作为index/tag的,如果在进程切换的时候不希望整个被flush掉,也需要含有PCID/ASID。
其实,页表既然是放在普通内存中的,自然也可以被缓存到普通cache中,MMU在不得不去查找页表之前,也会先去普通cache里看看,实在没有再极不情愿的去访问内存。以x86处理器为例,CR3寄存器和每级页表的entry都含有一个叫PCD(page-level cache disable)的位,可以控制该entry对应的下级页表(对于PTE对应的就是page)是否需要被缓存到普通cache中。
我的理解是,这种cache(intel的手册中称为page-level cache)缓存的是一个页表,而paging structure caches缓存的是页表中的某一项,后者效率更高,所以尝试访问的顺序应该是TLB -> paging structure caches -> 普通cache中的页表 -> 内存中的页表。
转载:虚拟地址转换[五] - paging structure caches
✅ 在x86-64(IA-32e) 架构中的四级页表名称与缩写
Intel的64位架构使用四级页表,分别如下:
| 层级 | 缩写 | 全称 | 每项控制多少内存 |
|---|---|---|---|
| 1 | PML4E | Page Map Level 4 Entry | 控制512 GiB |
| 2 | PDPTE | Page Directory Pointer Table Entry | 控制1 GiB |
| 3 | PDE | Page Directory Entry | 控制2 MiB |
| 4 | PTE | Page Table Entry | 控制4 KiB(标准页大小) |
它们组成了64位虚拟地址的逐级映射:
1
2
3
4
5
6
[VA 63–48] ignored
[VA 47–39] → PML4E
[VA 38–30] → PDPTE
[VA 29–21] → PDE
[VA 20–12] → PTE
[VA 11–0 ] → Offset within 4KiB page
✅ 在Linux内核术语中对应的名称(更常见于内核源码)
Linux使用了更抽象、通用的术语来表示页表层级,特别是为了跨平台兼容。
| 层级(从上到下) | Linux名称 | 对应于x86-64硬件名称 | 含义/缩写解释 |
|---|---|---|---|
| 1 | PGD | PML4 | Page Global Directory |
| 2 | P4D | 一般恒等于PGD(内核中保留) | Page 4th Directory |
| 3 | PUD | PDPTE | Page Upper Directory |
| 4 | PMD | PDE | Page Middle Directory |
| 5 | PTE | PTE | Page Table Entry |
❗注意:
PGD → P4D → PUD → PMD → PTE是Linux的通用叫法,x86-64实际只用到四级,但为了统一架构,Linux中保留了五级接口(有的层级恒等于上一层)。
⛓️ 举个流程图例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. CPU 访问虚拟地址 VA
2. 查 TLB:
- 如果命中 → 得到物理地址 → 成功!
- 如果未命中 → 进入 Page Walk:
┌─────────────┐
↓ │
查 PML4[va[47:39]] ←—— Paging Structure Cache 尝试加速
↓
查 PDP[va[38:30]] ←—— Paging Structure Cache
↓
查 PD[va[29:21]] ←—— Paging Structure Cache
↓
查 PT[va[20:12]]
↓
得到 PTE → 填入 TLB → 完成地址转换

