Могут ли два последовательных оператора присваивания в C быть выполнены на оборудовании не по порядку? - PullRequest
0 голосов
/ 20 ноября 2018

Учитывая следующую программу на 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] == 2 и vals[0] == 0?

Если это возможно, любое описание операций загрузки / сохранения, которые приведут к этому сценарию, будет высоко оценено.

РЕДАКТИРОВАТЬ 1: Добавлено немногобольше контекста к разделу кода.К сожалению, у меня нет машинного кода из скомпилированного источника.

Ответы [ 2 ]

0 голосов
/ 20 ноября 2018

Да, это возможно, потому что компилятор может переупорядочить эти операторы, как описано в Ответ Питера .

Однако, возможно, вы все еще задаетесь вопросом о другой половине: что может сделать аппаратное обеспечение . При условии, что ваши хранилища окажутся в сборке в порядке, указанном в исходном коде 1 , если прерывание происходит на том же процессоре, на котором выполняется этот код, изнутри прерывания вы увидите все в последовательном порядке. То есть из обработчика прерываний вы никогда не увидите, что второе хранилище завершено, а первое нет. Единственные сценарии, которые вы увидите, не завершены, оба выполнены или первый завершен, а второй нет.

Если задействовано несколько ядер, и прерывание может выполняться на другом ядре, тогда вы просто используете классические сценарии совместного использования потоков, будь то прерывание или нет - и то, что может наблюдать другое ядро, зависит от аппаратной памяти модель. Например, на относительно сильно упорядоченном x86 вы всегда будете наблюдать за магазинами по порядку, где, как на более слабо упорядоченных моделях памяти ARM или POWER, вы сможете увидеть магазины не по порядку.

В целом, однако, ЦП может выполнять все виды переупорядочения: порядок, который вы видите в обработчике прерываний, является особым случаем, когда ЦП восстанавливает вид последовательного выполнения в точке обработки прерывания. То же самое верно для любого случая, когда поток наблюдает за своими собственными хранилищами. Однако, когда хранилища наблюдаются другим потоком - то, что происходит затем, зависит от модели аппаратной памяти, которая сильно варьируется в зависимости от архитектуры.


1 Предполагая также, что они отображаются отдельно - ничто не мешает интеллектуальному компилятору заметить, что вы присваиваете смежные значения в памяти и, следовательно, преобразует два хранилища в одно шире один. Большинство компиляторов могут делать это по крайней мере в некоторых сценариях.

0 голосов
/ 20 ноября 2018

C не работает на оборудовании напрямую. Сначала он должен быть скомпилирован.

Особенности неопределенного поведения (например, несинхронизированное чтение неатомарных переменных) полностью зависят от реализации (включая переупорядочение во время компиляции в компиляторе и в зависимости от целевой архитектуры ЦП, правил переупорядочения во время выполнения того ISA) .

Чтение / запись неатомарных переменных не считается наблюдаемым побочным эффектом в C или C ++, поэтому их можно оптимизировать и переупорядочить до предела сохранения поведения программы в целом (кроме случаев, когда Программа имеет неопределенное поведение - в этом случае оптимизация может делать что угодно, даже если компилятор не может «видеть», что при компиляции будет UB.)

См. Также https://preshing.com/20120625/memory-ordering-at-compile-time/

...