指令获取访问传递锁定的指令

Instruction fetch accesses passing locked instructions

英特尔软件开发人员手册提到 "instruction fetch and page table accesses can pass locked instructions"。这是什么意思,为什么重要?

有一个 post 说许多 Windows 函数以 MOV EDI, EDI 指令开始,因为它对于安全代码挂钩很有用:它可以自动替换为两个 -字节相对跳转。但是如果fetch访问内存可以"pass locked instructions",会不会出现下面的情况?

会不会也有这样的事情发生?

来自 Intel 64 和 IA-32 架构软件开发人员手册,第 3 卷:"System Programming Guide"

Locked operations are atomic with respect to all other memory operations and all externally visible events. Only instruction fetch and page table accesses can pass locked instructions. Locked instructions can be used to synchronize data written by one processor and read by another processor.

For the P6 family processors, locked operations serialize all outstanding load and store operations (that is, wait for them to complete). This rule is also true for the Pentium 4 and Intel Xeon processors, with one exception. Load operations that reference weakly ordered memory types (such as the WC memory type) may not be serialized.

Link: Why do Windows functions all begin with a pointless MOV EDI, EDI instruction?

关于第二种情况 - "passing a locked instruction" 并不意味着它破坏了原子性。如果存储以原子方式写入这 2 个指令字节,则您在任何时候都看不到其中一个(存储将简单地对完整的缓存行进行操作 - 请注意,如果 2 个字节被拆分为 2 行,则它不会是原子的).它的意思是您为了尝试同步而放置的任何锁定指令都不会阻止代码获取,因此就内存排序而言 - 它可以发生在它之前或之后。

现在,关于第一种情况和一般问题 - 请注意,您的描述中没有锁定。您描述的情况是完全有效的,即使它是数据读取而不是代码读取 - 除了您自己强制执行的顺序之外,两个内核之间没有固有顺序。为了强制执行这样的顺序,您可以开始使用障碍和信号量,或任何其他方法,它最终会归结为一些锁阻塞 cpu 1 直到 cpu 0 表示写入已完成.

在那种情况下,数据读取会被锁停止,但较新的代码读取实际上可以获取旧数据,尽管您试图保护它。然而,这里出现了一种机制 x86 内核通常实现称为 SMC(自修改代码)刷新 - 来自 cpu 0 的存储监听 cpu 1 中的指令缓存,检测那里的陈旧代码,并且因为它无法确定这段代码在管道中的确切位置,或者可能已经产生了什么影响(据我们所知,那里可能有一个暂停指令,或者更糟)——它只会刷新整个管道。不同产品的具体细节可能不同,但这个概念很老了。

页面遍历情况稍微复杂一些,但这里还有一种机制可以检测使用过程中的大多数修改情况 - 查找“TLB shootdown”。请注意,在某些情况下,SMC 和 TLB 修改during 运行 完全有效并且服务于一个目的(SMC 经常用于 JITting,页面移动是一种无需复制即可在进程之间传递数据的廉价方式。