В asm нет проблем: выровненные загрузки / хранилища, выполненные с помощью одной инструкции на x86, имеют атомарную ширину до qword (8 байт). Почему целочисленное присваивание для естественно выровненной переменной атомарно в x86?
(На 486 гарантия распространяется только на 4-байтовые выровненные значения, а может даже и на 386, поэтому, возможно, именно поэтому Xv6 использует блокировку? Я не уверен, что она должна быть многоядерной безопасной на 386. Насколько я понимаю, на редких компьютерах 386 SMP не совсем реализована современная модель памяти x86 (упорядочение памяти и т. Д.).
Но C не является asm. Использование простой не-atomic
переменной из нескольких «потоков» одновременно является неопределенным поведением, если только все потоки не только читают. Это означает, что компиляторы могут предполагать, что обычная переменная C не изменяется асинхронно другими потоками.
Использование ticks
в цикле в C позволит компилятору прочитать его один раз и постоянно использовать одно и то же значение . Вам нужен макрос READ_ONCE
, который используется в ядре Linux, например, *(volatile int*)&ticks
. Или просто объявите это как volatile unsigned ticks;
Для переменной, достаточно узкой, чтобы поместиться в один целочисленный регистр, вероятно, можно с уверенностью предположить, что здравомыслящий компилятор запишет ее с одним хранилищем слов, будь то mov
или место назначения памяти inc
или add dword [mem], 1
. (Вы не можете предполагать, что компилятор будет использовать место назначения памяти inc / add, поэтому вы не можете зависеть от приращения одноядерного атома по отношению к прерываниям.)
С одним писателем и несколькими читателями, да, читатели могут просто читать его без какой-либо блокировки, если они используют volatile
.
Даже в portable ISO C, volatile sig_atomic_t
имеет некоторые очень ограниченные гарантии безопасной работы при записи обработчиком сигнала и чтении потоком, который выполнял обработчик сигнала. (Не обязательно для других потоков, хотя: в ISO C volatile
не избегает гонки данных UB. Но на практике на x86 с не враждебными компиляторами это нормально.)
(Сигналы POSIX являются эквивалентом прерываний в пользовательском пространстве).
См. Также Может ли num ++ быть атомарным для 'int num'?
Чтобы один поток опубликовал более широкий счетчик в две половины, вы обычно используете SeqLock. С 1 записывающим устройством и несколькими считывающими устройствами нет никакой реальной блокировки, просто повторите действия читателей, если запись перекрывается с их чтением. См. Реализация 64-битного атомного счетчика с 32-битным атомом