Ant*_*ala 23 paging x86 x86-64 virtual-memory tlb
我是一个操作系统爱好者,我的内核运行在80486+,并且已经支持虚拟内存.
从80386开始,英特尔的x86处理器系列及其各种克隆通过分页支持虚拟内存.众所周知,当设置PG
位时CR0
,处理器使用虚拟地址转换.然后,CR3
寄存器指向顶级页面目录,即用于将虚拟地址映射到物理地址的2-4级页表结构的根.
处理器不会为生成的每个虚拟地址查询这些表,而是将它们缓存在名为Translation Lookaside Buffer或TLB的结构中.但是,当对页表进行更改时,需要刷新TLB.在80386处理器上,此刷新将通过使用顶级页面目录地址或任务切换重新加载(MOV
)来完成CR3
.这应该无条件地刷新所有TLB条目.据我所知,虚拟内存系统在任何更改后总是重新加载CR3是完全有效的.
这是浪费,因为TLB现在会抛出完全好的条目,因此在80486处理器INVLPG
中引入了指令.INVLPG
将使与源操作数地址匹配的TLB条目无效.
然而,从Pentium Pro开始,我们还拥有全局页面,这些页面不会被移动到CR3
任务切换; 和AMD x86-64 ISA表示某些高级页面表结构可能会被高速缓存而不会失效INVLPG
.为了获得每个ISA所需内容和不需要内容的连贯图片,我们真的需要为80年代以来发布的大量ISA下载1000页的数据表来阅读其中的几页,即使这样,文档似乎也是如此对TLB失效特别模糊,如果TLB未正确无效,会发生什么.
为简单起见,可以假设我们正在讨论单处理器系统.此外,可以假设在更改页面结构后不需要任务切换.(因此INVLPG
总是被认为至少是重新加载CR3
寄存器的好选择).
基本假设是CR3
每次更改页面表和页面目录后都需要重新加载,这样的系统是正确的.但是,如果想要避免不必要地冲刷TLB,则需要回答2个问题:
如果INVLPG
ISA支持,经过哪种更改可以安全地使用它而不是重新加载CR3
?例如"如果一个取消映射一个页面框架(将相应的表条目设置为不存在),则可以始终使用INVLPG
"?
在不触及CR3
或执行的情况下,可以对表和目录进行哪些更改INVLPG
?例如"如果一个页面根本没有映射(不存在),那么就可Present=1
以为它编写一个PTE 而不用刷新TLB"?
即使在阅读了大量的ISA文档以及与INVLPG
Stack Overflow 相关的所有内容之后,我也不确定我在那里提供的任何一个例子.事实上,一篇值得注意的帖子马上说:"我不知道你何时应该使用它,什么时候不应该使用它." 因此,您可以提供任何特定的,正确的示例,最好是文档,以及您可以给出的IA32或x86-64.
Bre*_*dan 24
用最简单的术语; 要求是CPU的TLB可以记住的任何已经改变的东西必须在依赖于改变的任何事情发生之前失效.
CPU可以记住的东西包括:
警告:由于Intel CPU不记得"不存在"页面,因此英特尔提供的文档可能表示将页面从"不存在"更改为"存在"时无需无效.英特尔的文档仅适用于英特尔CPU.对于所有80x86 CPU都不正确.某些CPU(主要是Cyrix)确实记得页面"不存在"时,并且由于那些CPU在将页面从"不存在"更改为"存在"时必须使其无效.
请注意,由于投机执行,你不能偷工减料.例如,如果您知道某个页面从未被访问过,则您不能认为它不在TLB中,因为TLB可能已被推测性地获取.
我非常仔细地选择了"在依赖变化的任何事情发生之前".现代CPU(特别是对于长模式)会缓存更高级别的分页结构(例如PDPT条目),而不仅仅是最终页面.这意味着如果更改更高级别的分页结构但页表条目本身保持不变,则仍需要使其无效.
它还意味着如果没有任何依赖于更改,则可以跳过失效.一个简单的例子是访问和脏标志 - 如果你不依赖这些标志(确定"最近最少使用"和哪些页面要发送到交换空间)那么如果CPU没有那么无关紧要没有意识到你已经改变了它们.如果CPU使用旧的/陈旧的TLB信息(页面错误),如果出现页面错误,也可以(不推荐用于单CPU但非常推荐用于多CPU)跳过TLB失效当且仅当实际需要时,处理程序才会失效.
此外; "CPU的TLB可以记住的任何东西"都有点棘手.操作系统通常会将分页结构本身映射到虚拟地址空间,以便快速/轻松地访问它们(例如,您假装页面目录是页表的常见"递归映射"技巧).在这种情况下,当您更改页面目录条目时,您需要使受影响的普通页面无效(正如您所期望的那样),但您还需要使任何映射中发生的更改无效.
要使用(INVLPG或重新加载CR3),有几个问题.对于单页,INVLPG会更快.如果您更改页面目录(影响1024页或512页,取决于哪种分页风格),那么在循环中使用INVLPG可能会或者可能不会更贵,只需重新加载CR3(它取决于CPU /硬件和访问模式)对于失效后的代码).
还有2个问题.第一个是任务切换.在使用不同虚拟地址空间的任务之间切换时,您必须更改CR3.这意味着如果您更改了影响大区域的内容(例如页面目录),您可以通过提前执行任务切换来提高整体性能,而不是立即重新加载CR3(用于失效),然后很快重新加载CR3(用于任务切换) ).基本上,它是"一石二鸟"的优化.
另一件事是"全球网页".通常,所有虚拟地址空间(例如内核)中的页面都相同.当您重新加载CR3时(例如,在任务切换期间),您不希望保持相同的页面的TLB无缘无故地失效,因为这会对性能造成不必要的影响.为了解决这个问题并提高性能,(对于Pentium和更高版本)有一个名为"全局页面"的功能,您可以将这些常用页面标记为全局,并且在重新加载CR3时它们不会失效.在这种情况下,如果您需要使全局页面无效,则需要使用INVPLG或更改CR4(例如,禁用然后重新启用全局页面功能).对于较大的区域(例如,更改页面目录而不仅仅是一个页面),它与以前相同(在循环中使用CR4可能比INVLPG更快或更慢).
对于你的第一个问题:
INVLPG
并且可以进行任何可能的更改。使用INVLPG
总是安全的。CR3
不会使 TLB 中的全局页面失效。所以有时候你必须使用INVLPG
,因为重新加载CR3
没有效果。INVLPG
必须用于涉及的每个页面。如果您一次更改多个页面,那么重新加载会CR3
比多次INVLPG
调用更快。对于你的第二个问题:
未映射的页面无法缓存在 TLB 中(假设您之前取消映射时已正确使其失效)。因此,任何不存在的更改都不需要INVLPG
或CR3
重新加载。
归档时间: |
|
查看次数: |
4279 次 |
最近记录: |