【发布时间】:2016-01-26 05:38:21
【问题描述】:
我在本网站和其他地方阅读过类似的答案,但在某些情况下仍然感到困惑。
我知道标准实际上向我们保证了什么,我了解关键字的预期用途,并且我很清楚编译器缓存和 L1/L2/ect 之间的区别。缓存;出于好奇,我了解其他情况。
假设我在 C 中声明了一个变量 volatile。四种情况:
-
信号处理程序,单线程(按预期):这是关键字旨在解决的问题。我的进程从操作系统获得信号回调,并且我在进程的正常执行之外修改了一些
volatile变量。由于它被声明为volatile,因此正常进程不会将此值存储在 CPU 寄存器中,而是始终从内存中加载。即使信号处理程序写入volatile变量,因为信号处理程序与正常进程共享相同的地址空间,即使volatile变量之前缓存在硬件中(即L1, L2),我们保证主进程将加载正确的、更新的变量。太好了,大家都很开心。 -
DMA 传输,单线程:假设
volatile变量映射到正在进行 DMA 写入的内存区域。和以前一样,编译器不会将volatile变量保存在CPU 寄存器中,而是总是从内存中加载;但是,如果该变量存在于 hardware 缓存中,则加载请求将永远不会到达主内存。如果 DMA 控制器在我们背后更新 MM,我们将永远无法获得最新值。在抢占式操作系统中,最终我们可能会被上下文切换出来,并且下一次我们的进程恢复时,缓存将是冷的,我们实际上必须从主内存重新加载。我们将获得正确的功能......最终(我们自己的进程也可能会交换该缓存线 - 但同样,我们可能会在此之前浪费宝贵的周期)。当通过 DMA 控制器更新主内存时,是否有标准化的硬件支持或操作系统支持通知硬件缓存?或者我们是否必须显式刷新缓存以确保我们不会读取错误值? (这在列出的架构中是否可行?) -
内存映射寄存器,单线程: 与 #2 相同,除了
volatile变量映射到内存映射寄存器(或显式 IO 端口)。我想这是一个比 #3 更困难的问题,因为至少 DMA 控制器会在传输完成时向 CPU 发出信号,这让操作系统或硬件有机会做某事。 -
Mutilthreaded:如果我有一个
volatile变量,是否可以保证在不同物理内核上运行的多个线程之间的缓存一致性?可以肯定的是,编译器仍在从内存中发出加载指令,但是如果该值缓存在一个内核的缓存中,是否可以保证相同的值必须存在于另一个内核的缓存中? (我想这对于同一物理核心上不同逻辑核心上的超线程线程来说根本不是问题,因为它们共享物理缓存)。我压倒性的直觉说不,但我想我还是会在这里列出这个案例。
如果可能,区分 x64 和 ARMv6/7/8 架构,以及内核与用户空间解决方案。
【问题讨论】:
-
c/c++
volatile与硬件无关。它只是为编译器标记变量。编译器不会重新排序对标记变量的访问,也不会优化对它们的访问。 -
@MichaelNastenko 我想清楚了我知道。也许我应该清楚我正在寻找是否有可用的操作系统/架构支持可以在 C 标准范围之外被编译器利用
-
volatile表示“不重新排序,不优化”。不多也不少。您提到的#1-#3 场景由硬件解决。 #4 通常由 MESI 或任何其他缓存一致性协议解决,c/c++volatile在这种情况下是无用的。
标签: c multithreading caching