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

时间:2018-11-19 22:50:06

标签: 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 );
}

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

如果可能的话,将不胜感激对导致这种情况的加载/存储操作的任何描述。

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

2 个答案:

答案 0 :(得分:10)

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

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

在C或C ++中,非原子变量的读/写操作不被视为可观察到的副作用,因此可以对其进行优化和重新排序,以保持程序整体的行为(除非程序具有未定义的行为-在那种情况下,即使编译器无法“看到”,优化也可以做任何事情。)

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

答案 1 :(得分:4)

是的,这是可能的,因为编译器可能会按Peter's answer中所述对那些语句重新排序。

但是,您可能仍然想知道另一半:硬件可以做什么。假设您的存储按照您在source 1 中显示的顺序结束在程序集中,如果在运行此代码的同一CPU上发生中断,您将看到该中断一切都以一致的顺序进行。也就是说,在中断处理程序中,您将永远看不到第二个存储已完成,而第一个存储则未完成。您将看到的唯一情况都是尚未完成,都已完成或第一个尚未完成,而第二个还没有完成。

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

但是,通常,CPU可能会进行各种重新排序:您在中断处理程序中看到的排序是一种特殊情况,其中CPU将在处理中断时恢复顺序执行的外观。在线程观察其自己的存储的任何情况下都是如此。但是,当通过不同线程观察存储时-发生什么情况则取决于硬件内存模型,这在体系结构之间存在很大差异。


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