在具有硬件 TLB 管理的微处理器(例如 Intel x86-64)上,如果发生 TLB 未命中并且处理器正在遍历页表,这些(片外)内存访问是否会通过缓存层次结构(L1、L2 等)? )?
是否缓存了页表遍历?
是的,据我所知,在 Intel x86-64 处理器上,当发生 TLB 未命中并且处理器正在遍历页表时,这些片外内存访问会通过缓存层次结构。
我对一些细节仍然有些模糊,我希望其他一些答案能填补它们——难道没有英特尔或 AMD 手册详细描述了页面遍历吗?我的理解是:
- 某个地址寄存器中的虚拟地址首先被移交给快速 TLB 以转换为物理地址——PC 中的地址被移交给 L1 ITLB,任何其他寄存器中的地址被移交给 L1 DTLB .
- 如果第一次查找失败,则会尝试另一级别的更慢、更大的 TLB。(此 L2 TLB 是否也分为 ITLB 和 DTLB,还是统一的 TLB 缓存?是否还有其他 TLB 级别 - L3?L4?)
- 如果 TLB 查找完全失败,并且 x86 和 x86-64 VHPT walker 被禁用,CPU 会发出 TLB 未命中错误信号,该错误会被 OS 内核拦截。我的理解是,几乎所有非 x86 CPU 都做同样的事情——完全在软件中处理 TLB 未命中。如果启用,x86 和 x86-64 处理器具有硬件辅助的 VHPT 表遍历器,可以处理接下来的几个步骤。(x86 和 x86-64 芯片是否有一个位可以完全禁用 VHPT,或者是否有很多位可以为某些地址范围启用 VHPT 而对其他地址范围禁用 VHPT?这些位在哪里?)
- 如果 TLB 查找完全失败,则原始(可能是用户模式)虚拟地址 V1 将转换为 V2,即页表条目 PTE 的虚拟地址,其中包含 V1 的物理页号。
- 因为 V2 又是一个虚拟地址,所以 CPU 会进行正常的虚拟到物理地址转换,除了它跳过 L1 并直接转到 L2。
- 硬件在 TLB 中查找虚拟地址 V2 与从(虚拟索引的)L2 缓存中获取该 PTE 并行。
- 因为V2不是一条指令的地址,所以不经过L1指令缓存;并且因为V2不是普通用户数据的地址,所以不经过L1数据缓存。V2 最初馈入 L2 统一缓存(统一指令+数据+PTE 缓存)。请参阅“缓存层次结构示例”。
- 如果 L2 缓存(或 L3 或任何其他虚拟索引缓存)包含 PTE,则 VHPT 从缓存内存中获取 PTE 并将 V1 的 PTE 安装到 TLB 中,并且该 PTE 中的物理地址用于转换原始虚拟地址 V1 到物理 RAM 地址,最终完全在硬件中获取该数据或指令,而无需操作系统的任何帮助。
- 如果所有级别的虚拟索引缓存都失败了,但 V2 的第二次 TLB 查找成功,则 VHPT 从物理索引缓存或主内存中获取 PTE,将 V1 的 PTE 安装在 TLB 中,并将物理地址安装在该 TLB 中。 PTE 用于将原始虚拟地址 V1 转换为物理 RAM 地址,最终完全在硬件中获取该数据或指令,而无需操作系统的任何帮助。
- 如果第二次 TLB 查找失败,硬件 VHPT walker 将放弃 VHPT TRANSLATION FAULT。
- 当 VHPT TRANSLATION FAULT 发生时,CPU 会陷入操作系统。操作系统必须找出问题所在并解决问题:
- (a) 可能包含 V2 的页面当前已换出到磁盘,因此操作系统将其读入 RAM 并重新启动失败的指令,或者
- (b) 可能有错误的程序正在尝试读取或写入或执行某些无效位置,并且操作系统终止了该进程,或者
- (c) 操作系统编写者使用这种机制来捕获各种访问的各种其他技巧——加载包含可能被换出到磁盘的 V1 的页面;用于调试新程序的各种陷阱;在不直接支持它的 CPU 上模拟“W^X”;支持写时复制;等等。
Thomas W. Barr、Alan L. Cox、Scott Rixner 第 2 页上的图表。 “翻译缓存:跳过,不要走(页表)” 在“MMU 缓存存储的条目”和“L2 数据缓存存储的条目”之间划清界限。(对于设计新 CPU的人来说,这可能是一篇有用的论文,这完全是“电子设计”的主题)。
斯蒂芬·埃兰尼安和大卫·莫斯伯格。 “IA-64 Linux 内核中的虚拟内存” 和 Ulrich Drepper。 “每个程序员都应该知道的关于内存的知识” (对于编写处理 IA-64 页表的操作系统的人来说,这可能是一篇有用的论文,这对于 ED 来说有点离题——也许 Stack Overflow 的“操作- system”标签或“osdev”标签或OSDev.org wiki 将是该主题的更好位置)。
英特尔第 533 页上的表 A-10。 “英特尔® 64 和 IA-32 架构软件开发人员手册” “PAGE_WALKS.CYCLES ... 可以提示大多数页面遍历是否被缓存满足或导致 L2 缓存未命中。”
我倾向于同意这属于计算机架构 stackexchange,而不是电子 stackexchange,但因为这里是:
@davidcary 是正确的。
一些历史:
Intel x86 页表遍历一直没有缓存到 P5,也就是 Pentium。更准确地说,页表遍历内存访问没有被缓存,绕过了缓存。由于当时大多数机器都是直写的,因此它们接收到的值与缓存一致。但他们没有窥探缓存。
P6,又名 Pentium Pro 和 AFAIK 所有后续处理器页表遍历都被允许访问缓存,并使用从缓存中提取的值。因此,他们使用回写式缓存。(当然,您可以将页表放在不可缓存的内存中,例如由 MTRR 定义。但这是一个很大的性能损失,尽管它对于调试操作系统很有用。)
顺便说一句,“页表遍历内存访问可能访问数据缓存”与“页表条目可能存储(缓存)在 TLB Ttranslation Lookaside Buffer 中)是分开的。在某些机器上,TLB 被称为“翻译缓存”。
另一个相关问题是页表的内部节点可能被缓存在更多类似 TLB 的数据结构中,例如 PDE 缓存。
一个关键区别:数据缓存是连贯的,并且是被窥探的。但是 TLB 和 PDE 缓存没有被窥探,即不连贯。底线是,由于页表可能缓存在不连贯的 TLB 和 PDE 缓存等中,当页表条目可能已经如此时,软件必须显式刷新单个条目或批量组(如整个 TLB)缓存被更改。至少在以“危险”方式更改时,从 RW->R->I 或更改地址。
我认为可以公平地说,每次添加一种新型的非连贯的类似 TLB 的缓存时,一些操作系统就会崩溃,因为它有隐含的假设,即没有这样做。