В asm нет проблем: выровненные загрузки / сохранения, выполненные с помощью одной инструкции на x86, являются атомарными до ширины qword (8 байт). Почему целочисленное присваивание выполняется с естественным выравниванием переменная atomic на 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
.
Даже в портативном ISO C в volatile sig_atomic_t
есть очень ограниченные гарантии безопасной работы при написании обработчиком сигнала и чтении потоком, запустившим обработчик сигнала. (Не обязательно другими потоками: в ISO C volatile
не избегает UB гонки данных. Но на практике на x86 с не враждебными компиляторами это нормально.)
(Сигналы POSIX являются эквивалентом прерываний в пользовательском пространстве.)
См. Также Может ли num ++ быть атомарным вместо int num?
Чтобы один поток опубликовал более широкий счетчик из двух половин, вы обычно используете SeqLock. С 1 записывающим устройством и несколькими считывателями фактической блокировки нет, просто повторите попытку считывателя, если запись перекрывается с их чтением. См. Реализация 64-битного атомного счетчика с 32-битным атомаром
person
Peter Cordes
schedule
04.06.2019
tick
в цикле в C позволит компилятору прочитать его один раз и повторно использовать одно и то же значение. Вам понадобится макросREAD_ONCE
, который используется в ядре Linux, например*(volatile int*)&tick
. Но да, для переменной, достаточно узкой, чтобы поместиться в один целочисленный регистр, обычно можно с уверенностью предположить, что нормальный компилятор запишет ее с одним хранилищем двойных слов. С одним писателем и несколькими читателями, да, читатели могут просто читать его без какой-либо блокировки. - person Peter Cordes   schedule 26.03.2019ticks
вvolatile unsigned ticks
. Ссылки по теме: Почему целочисленное присвоение естественно выровненной переменной атомарно на x86? / Программирование MCU - оптимизация C ++ O2 прерывается, пока цикл. С одним писателем вы правы, что не делаете нужен атомарныйinc
, только для того, чтобы его часть хранилища была атомарной. Но см. Может ли num ++ быть атомарным для 'int num'? на случай, если вы любопытно. - person Peter Cordes   schedule 26.03.2019