Post

虚拟地址转换[五] - paging structure caches

回顾上文TLB entryPTE entry的组成,你有没有意识到,在多级页表系统中,TLB其实只是最后一级PTE的缓存(对于large pagesTLB则最后一级是PDE或者PDPTE,本文以下的讨论都是针对非large page的情况),这和在单级页表中是一样的。

多级页表的查找是一个串行的,链式的过程。试想一下,访问在虚拟地址空间里连续的两个pages(比如虚拟地址分别为0x00001234567890000x000012345678A000),而这两个pagesPGD 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)。

除了最后一级页表PTEentry是直接指向page外,其他级的页表的entry都是指向下一级页表首地址的,因此这些级的页表被称为paging structure,所以PDE cachePDPTE cachePML4 cache被统称为paging structure caches。在ARM中,这些caches被称为table walk caches(名字应该是来自MMU里的table walk unit)。

如果发生TLB miss,相当于PTE cache中没找到,那就

  1. PDE cache中找,如果找到了则获得对应PT页表的首地址,可继续在PT中索引到PTE,需要1次内存访问;
  2. 没找到再从PDPTE cache中找,如果找到了则获得对应PD页表的首地址,然后在PD->PT中索引,需要2次内存访问;
  3. 还没找到再从PML4 cache中找,如果找到了则获得对应PDPT页表的首地址,然后在PDPT->PD->PT中索引,需要3次内存访问;
  4. PML4 cache中也没有的话,那只能去DRAM里按照PML4->PDPT->PD->PT找了,需要4次内存访问。

可见,从TLBDRAM,越往后,所需要的内存访问次数越多。

img

TLB一样,paging structure caches需要使用专用的cache来实现,对这些cache的支持与否取决于不同处理器的具体设计。以intelHaswell为例(i7-4770)为例,其PDE cache含有32个entries

这篇文章曾讲到TLB中可能含有PCID/ASID来分别不同的进程,同样的,paging structure caches也是使用虚拟地址的位域子集作为index/tag的,如果在进程切换的时候不希望整个被flush掉,也需要含有PCID/ASID

其实,页表既然是放在普通内存中的,自然也可以被缓存到普通cache中,MMU在不得不去查找页表之前,也会先去普通cache里看看,实在没有再极不情愿的去访问内存。以x86处理器为例,CR3寄存器和每级页表的entry都含有一个叫PCDpage-level cache disable)的位,可以控制该entry对应的下级页表(对于PTE对应的就是page)是否需要被缓存到普通cache中。

img

我的理解是,这种cacheintel的手册中称为page-level cache)缓存的是一个页表,而paging structure caches缓存的是页表中的某一项,后者效率更高,所以尝试访问的顺序应该是TLB -> paging structure caches -> 普通cache中的页表 -> 内存中的页表

​ 转载:虚拟地址转换[五] - paging structure caches


✅ 在x86-64IA-32e) 架构中的四级页表名称与缩写

Intel的64位架构使用四级页表,分别如下:

层级缩写全称每项控制多少内存
1PML4EPage Map Level 4 Entry控制512 GiB
2PDPTEPage Directory Pointer Table Entry控制1 GiB
3PDEPage Directory Entry控制2 MiB
4PTEPage 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硬件名称含义/缩写解释
1PGDPML4Page Global Directory
2P4D一般恒等于PGD(内核中保留)Page 4th Directory
3PUDPDPTEPage Upper Directory
4PMDPDEPage Middle Directory
5PTEPTEPage Table Entry

❗注意:PGD → P4D → PUD → PMD → PTELinux的通用叫法, 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 → 完成地址转换
This post is licensed under CC BY 4.0 by the author.