如何在 x86 上读取过时的值
Posted
技术标签:
【中文标题】如何在 x86 上读取过时的值【英文标题】:How to read stale values on x86 【发布时间】:2019-04-08 02:49:15 【问题描述】:我的目标是在没有缓存一致性的情况下读取过时和过时的内存值。我尝试使用prefetchnta
执行非临时加载,但它未能获取过时的值。我正在考虑执行某种流式内存到内存直接内存访问,但由于继续我当前的项目所需的大量背景知识,我遇到了一些麻烦。目前我正试图搞乱udmabuf,但即使这样也进展缓慢。应该注意的是,理想情况下,我想忽略 所有 CPU 缓存的内容,包括当前的 CPU。
提供我的理由:我正在开发可用于证明为非易失性存储器编写的程序的正确性的软件。由于 CPU Cache 是易失性的,CPU 的回写式缓存仍然是易失性的,需要观察它们如何写回内存的任意性质。
如果有人能给我一些关于如何进行的指示,我将不胜感激。我不介意深入研究 Linux 内核,事实上我现在正在这样做,也不介意修改它,我只需要一些正确方向的指导。
【问题讨论】:
我不确定我是否理解这里的缓存是如何涉及的,但是如果你想完全绕过它,完全禁用它会不会更容易?我想不出绕过缓存值的方法,但我会尝试将同一页面映射为 UC/UC- 并尝试使用它。如果 CPU 仍然以某种方式读取缓存行或将其写回(而您不希望这样),则可能来自另一个内核。 IIRC Intel 警告开发人员关于不同缓存类型的多个映射,在这种情况下这可能确实很好。 陈旧的价值应该从哪里来?内存还是缓存? NVM 是指二级存储还是持久内存?你的程序基本上是如何运作的? Louis,您应该仔细阅读英特尔 SDM 中有关缓存的部分,如果仍有疑问,请返回此处。 如果内存快照包含陈旧值,那么陈旧值如何来自缓存?当存储到持久内存时,最新的值将始终在缓存中,并且可能在内存中。我看不到缓存如何包含陈旧的值,但内存包含最新的值。您是说将捕获内存快照并且可能包含陈旧值(这很有意义),但陈旧值将来自缓存(这对我来说没有意义)。 请注意,您可以在拍摄快照后强制终止进程,然后查看是否可以正确地从该快照恢复。 【参考方案1】:我没有玩过这个,但我从文档中了解到,对于加载(与 NT 存储不同),没有什么可以绕过缓存或覆盖内存类型的强排序,如普通 WB(回写)。甚至 NT 也存储了已经缓存的数据,因此它们不会破坏这个或另一个已经为你正在编写的行缓存数据的内核的一致性。
您可以从 WC(写入组合)内存区域(使用 prefetchnta 或 SSE4 movntdqa)执行弱排序加载,但它们可能在物理地址级别仍然是一致的。
@MargaretBloom 评论了
IIRC 英特尔警告开发人员使用不同缓存类型的多个映射,这在这种情况下可能确实很好。
因此,也许您实际上可以通过同一物理页面的多个虚拟映射绕过缓存一致性。
我不知道是否仍然可以使用 PCI / PCIe 设备进行非连贯 DMA,但这可能是您在不通过缓存的情况下获取实际 DRAM 内容的唯一希望。 (现代 x86 系统上的大多数(?)DMA 是缓存一致的,这对性能有好处,并且可能因为内存控制器内置在 CPU 中。因此,在 Intel CPU 上,系统代理可以窥探 L3 标签以查看是否存在行缓存在芯片上的任何位置,与将请求发送到内存控制器并行。)
an INVD
instruction 使 所有 缓存无效而不首先进行回写,但我认为这包括共享 L3 缓存,可能还有所有其他内核的私有缓存。因此,您实际上无法在其他内核可能正在执行操作的 Linux 系统上使用它;您可能会通过使用它来破坏内核数据结构,以及在具有NVDIMMs 的机器上模拟您感兴趣的进程的电源故障。
如果您以某种方式使所有其他 CPU 内核脱机,并禁用仍在运行的一个内核上的中断
您可以wbinvd
(write-back+invalidate) 刷新所有缓存
然后运行一些被测代码
然后invd
看看是什么让它进入了 DRAM
然后重新启用中断。如果在wbinvd
和invd
之间处理了任何中断,则中断处理程序最终可能会缓存一些内核数据和内存中的一些数据,或者使设备驱动程序与硬件不同步。
更新:确实有人尝试过:
How to run "invd" instruction with disabled SMP support? How to explicitly load a structure into L1d cache? -invd
工作得非常好,以至于 printk
在设计错误的尝试中记录了一些有关它的信息,从而摧毁了一些商店。
【讨论】:
好主意! Linux 支持 CPU 热插拔。当设置缓存一致性不是要求时,PCIe 事务有一个“no snoop”属性位。但是他们并没有说这是禁止。我认为 PCIe 根复合体将尊重该位并将事务直接路由到内存控制器。 谢谢,您不仅回答了我的问题,还给了我三种不同的方法来获得解决方案。真的很感激,因为现在我有了前进的道路。以上是关于如何在 x86 上读取过时的值的主要内容,如果未能解决你的问题,请参考以下文章
x86 程序集:“subl”命令如何在 AT&T 语法中工作