CPU 缓存是否在 I/O 期间刷新到内存?

Are CPU caches flushed to memory during I/O?

假设我的服务器程序在不同的内核上有两个线程(T1 和 T2)运行。两者都服务于从单个外部客户端通过网络传入的 RPC。发生以下操作序列:

  1. 内存变量foo初始化为零
  2. 客户端发送RPC,恰好由T1服务,将foo设置为42
  3. T1 将值写入 foo,写入缓存在其核心的 L1(不是主内存)
  4. T1 向客户端发送 ACK
  5. 客户端发送RPC,恰好由T2服务,读取foo
  6. T2 从 它的 缓存或主内存中读取 foo 并看到它是零
  7. T2 回复客户说 foo 是零。

这违反了外部一致性。

这是否真的会发生,或者当 T1 执行 I/O 将 ACK 发送回客户端(第 4 步)时是否存在隐式刷新 T1 的缓存?

在 x86 上,缓存保持一致性以确保不会出现此类问题。

第一部分是每个缓存跟踪它保存的每一行的状态1。如果(使用你的例子)一条数据同时保存在两个缓存中,并且一个写入它,它将其缓存行设置为 "modified" 状态,并向另一个发送信号 CPU 告诉它将用于保存相同数据的缓存行设置为 "invalid" 状态。

谜题的第二部分是每个 CPU "snoops" 所有内存事务(通过其他 CPU 或总线主控 PCI 设备)所以它 "sees" 当其他人试图读取其缓存中的数据时。发生这种情况时,它会强制暂停该事务,将数据从其缓存中写入内存,然后让事务在数据写入后继续进行,以便获取当前数据。


  1. class 状态集是已修改、独占、共享和无效 (MESI)。大多数现代 CPU 至少增加了一种状态(通常是 "Owned",给出 MOESI),有些还增加了更多。尽管几乎所有内容都至少包括修改和无效。

在 x86 和 x64 系列上,所有缓存都是一致的,即使两个线程不共享同一个缓存单元,您也会在 T2 上获得 42。

你的思想实验可以进一步简化为2种情况:两个线程共享同一个缓存单元(多核)或不共享(多cpu)。

当它们共享一个缓存单元时,T1 和 T2 将使用相同的缓存,因此它们都将看到 42 而无需与内存进行任何同步。

如果缓存不共享(即多cpu),ISA 要求缓存单元同步,这对软件来说是透明的。两个线程都将在同一地址看到 42。虽然这种同步会引入一些开销,因此现在多核设计是首选(除了缓存昂贵的原因)。

在第3步,在T1修改值之前,它获取缓存行为"exclusive",这意味着它不存在于任何其他线程的缓存中,并将缓存行状态设置为"modified".

在第 6 步,T2 的缓存中没有该值,因此当它去获取该值时,缓存一致性协议会在 T1 的缓存中找到修改后的行。在T1的缓存和T2的缓存中缓存行的状态都设置为"shared"。