C中的两个顺序赋值语句是否可以在硬件上无序执行?

leo*_*eo1 5 c interrupt memory-model signal-handling

鉴于以下C程序:

static char vals[ 2 ] = {0, 0};

int main() {

char *a = &vals[0];
char *b = &vals[1];

while( 1 ) {

    SOME_STUFF()

    // non-atomic operations in critical section
    if( SOME_CONDITION() )
        {
        *a = 1;
        *b = 2;
        }
    else
        {
        *a = 0;
        *b = 0;
        }


    SOME_OTHER_STUFF()

    }

return 0;
}

int async_interrupt( void ) {

PRINT( a );
PRINT( b );
}
Run Code Online (Sandbox Code Playgroud)

硬件是否有可能首先将值实际加载2到内存位置&vals[1],以便中断例程可以执行并查看vals[1] == 2和vals[0] == 0?

如果可能的话,将非常感谢将导致此方案的任何加载/存储操作的描述.

编辑1:为代码部分添加了更多上下文.不幸的是,我没有编译源代码中的机器代码.

Pet*_*des 10

C不直接在硬件上运行.它必须先编译.

未定义行为的细节(如非原子变量的非同步读取)完全取决于实现(包括编译器中的编译时重新排序,并且取决于目标CPU体系结构,该ISA的运行时重新排序规则).

非原子变量的读/写在C或C++中不被视为可观察的副作用,因此它们可以被优化掉并重新排序到保持程序整体行为的极限(除非程序未定义)行为优化在这种情况下可以做任何事情,即使编译器在编译时不能"看到"UB也会有.)

另请参见https://preshing.com/20120625/memory-ordering-at-compile-time/

  • 正如我在你回答的帖子中所评论的那样:**C只保证因果关系适用于单个线程.**OP希望从中断处理程序*读取`vals [0]`和`vals [1]`*,它从主线程异步运行,因此如果没有同步读取`vals [0..1]`,并且没有该数组为`_Atomic`,则C不保证它会发现什么.`_Atomic`的重点是保证在这种情况下不是同步单线程执行的因果关系.根据非原子C变量的因果关系而提出的答案最多会产生误导. (2认同)

Bee*_*ope 4

是的,这是可能的,因为编译器可能会按照Peter 的回答中所述对这些语句重新排序。

然而,您可能仍然想知道另一半:硬件可以做什么。假设您的存储按照您在源1中显示的顺序最终出现在程序集中,如果运行此代码的同一 CPU 上发生中断,则从中断内您将看到所有内容都按一致的顺序排列。也就是说,在中断处理程序中,您永远不会看到第二个存储已完成,但第一个存储尚未完成。您将看到的唯一情况是两者均未完成、均已完成或第一个已完成而第二个未完成。

如果涉及多个核心,并且中断可能在不同的核心上运行,那么您只需经典的跨线程共享场景,无论是否是中断 - 另一个核心可以观察到的内容取决于硬件内存模型。例如,在相对强排序的 x86 上,您始终会按顺序观察存储,而在较弱排序的 ARM 或 POWER 内存模型上,您可能会看到存储无序。

然而,一般来说,CPU 可能会进行各种重新排序:您在中断处理程序中看到的排序是一种特殊情况,其中 CPU 将在处理中断时恢复顺序执行的外观。对于线程观察其自己的存储的任何情况都是如此。但是,当由不同的线程观察存储时,会发生什么取决于硬件内存模型,该模型在架构之间存在很大差异。


1还假设它们分别出现- 没有什么可以阻止智能编译器注意到您正在分配给内存中的相邻值,从而将两个存储转换为一个更宽的存储。大多数编译器至少在某些情况下可以做到这一点。