C 中的 volatile 关键字是否正确需要特殊的硬件支持才能工作?

Am I right volatile keyword in C needs special hardware support to work?

我了解 volatile 的作用和不作用,以 this question

中的示例为例
void waitForSemaphore()
{
   volatile uint16_t* semPtr = WELL_KNOWN_SEM_ADDR;/*well known address to my semaphore*/
   while ((*semPtr) != IS_OK_FOR_ME_TO_PROCEED);
}

我的问题是:在cpu缓存存在的情况下,volatile不能保证上述工作,因为它只强制cpu从内存中读取sme​​Per,而cpu不会'不知道内存是在 RAM 中还是在其中一个缓存中。因此,如果另一个设备更改了 WELL_KNOWN_SEM_ADDR 的内容,waitForSemaphore 不一定知道。所以一定有别的东西让它起作用。

我读过 this and this,似乎 volatile 本身并不足以保证这样的程序工作,必须有一些依赖于平台的魔法可以通过 L1/2/3 缓存或强制刷新它们,我我对吗?如果是这样,是否在所有流行平台(例如 x86)上都提供此类支持?

volatile 本身只告诉编译器 "assume that when you store to a volatile object, someone might take notice, so you can't optimise the store away" 和 "assume that when you read a volatile object, the result might not be the same as what you stored, and also someone might take notice that you read the volatile object, so you can't optimise the read away, and you must use the value read and not what you think should be stored there"。

例如,如果你写

int x = 0 * (*p);

编译器不能说 "I'll set x to 0 whatever *p is, so I don' need to bother reading *p"。如果 *p 是易变的,这是错误的。

C11 中有一些新功能可以帮助您,或者您可能想要使用适当的 OS 功能。

volatile 禁止编译器优化对此类变量的访问或对所有易失性变量重新排序此类访问(仅!)。实际语义可能是实现定义的(但必须由编译器指定)。

对于硬件:是的,缓存和总线缓冲区(例如 "write buffers")可能仍会重新排序,更不用说 CPU(s) 了。 volatile 并不意味着 fences/barriers。因此,底层硬件至少必须将此类内存区域标记为“(强)有序”。如果涉及外部硬件,则该区域也必须标记"uncached",因此每次访问都会直接访问硬件。这可能也适用于其他 CPUs,除非系统中有某种 "snooping" 硬件(每个 CPU 都会收到另一个 CPU 中缓存更改的通知)。

C11 提供 stdatomic.h(可选)。这将更适合 thread/interrupt 同步。如果有多个 CPUs,那么 volatile 就几乎没用了。但是,它仍然有硬件应用(可能需要额外的互斥锁或对外围设备的隐式独占访问)。

  1. 不,volatile 本身不需要硬件支持即可工作。有关详细信息,请参阅 gnaster729 的回答。

  2. 缓存同步问题与所谓的 "Memory model" (https://en.wikipedia.org/wiki/Memory_ordering ); if describing it very short - there is no such thing as "if another device has changed variable" (it is not observable from reading side), there is only a thing "if another device changed variable a earlier than variable b" - this IS observable. To deal with it, so-called "memory fences" a.k.a. "memory barriers" are used (https://en.wikipedia.org/wiki/Memory_barrier and https://www.kernel.org/doc/Documentation/memory-barriers.txt ) 有关,如果我没记错的话,它在 C++11 中有特殊支持。在 x86 上,内存栅栏隐含在 CAS 指令中; x86 被广泛认为符合所谓的 TSO(总存储排序)内存模型,该模型非常严格并且是最容易处理的模型之一(尽管如上文 kernel.org 参考所述,完全可行处理 any 内存模型下的正确性)。