Можно ли разделить выровненную целочисленную переменную, не превышающую естественное слово процессора, с изменчивым квалификатором между основной программой и ISR в C? Гарантировано ли, что никакие рваные чтения или записи не могут произойти?
Ключевое слово volatile
не подразумевает атомарность – это просто гарантирует, что переменная явно прочитана и не предполагается, что она не изменилась. Для безопасного совместного доступа без какого-либо другого механизма защиты переменная должна быть как атомарной, так и объявленной volatile
.
Компилятор может документировать типы, которые являются атомарными для любой конкретной цели, и может определять sig_atomic_t
для этой цели.
В целом, возможно, разумно предположить, что ваш компилятор не будет делать ничего извращенного и разбивать выровненное слово, где набор инструкций допускает атомное чтение. Следует соблюдать осторожность при переносе кода между платформами – такой код низкого уровня следует рассматривать как целевую и не переносную.
Что касается ключевого слова volatile
, то он просто защищает от возможных неправильных оптимизаций компилятором. Это не помогает в обеспечении безопасности streamов.
Независимо от того, является ли поточно-безопасным использование общей переменной заданного размера, до компилятора. Нет никаких гарантий того, что доступ является атомарным. Например, компилятор может загрузить переменную в регистр перед дальнейшей обработкой, а затем записать ее обратно в память. В основном это зависит от набора инструкций процессора. Если вы хотите быть уверенным, вам нужно будет проверить дизассемблированный код или написать код на ассемблере.
В противном случае вы можете сделать «мьютекс бедных» с помощью bool. Это работает только для конкретного случая ISR микроcontrollerов, которые не могут быть прерваны другими прерываниями. Поскольку вы знаете, что ISR не может быть прерван, вы можете сделать это:
static volatile bool busy; static volatile uint16_t shared; void isr (void) { if(!busy) { shared = something; } } void main (void) { ... busy = true; do_something(shared); busy = false; ... }
При таком подходе не имеет значения, являются ли busy
или shared
атомарными или нет. Независимо от того, где триггеры прерывания, shared
не будут уничтожены в середине доступа.
Нет никакой гарантии, что любая общая целочисленная переменная будет записана и прочитана атомарно. Если вам нужна такая гарантия, вы должны использовать тип sig_atomic_t
. Это единственный тип с такой гарантией.
Из стандарта C99 7.14:
2 Определенный тип
sig_atomic_t
который является (возможно, волатильным) целым типом объекта, к которому можно получить доступ как к атомному объекту, даже при наличии асинхронных прерываний.
Проверьте свой компилятор. ISR нестандартны. Кроме того, у C нет реального понятия «естественное слово процессора», кроме, быть может, int
.
Ответ «иногда». Если либо ISR, либо основной процесс изменяет переменную, тогда вы в порядке. Однако, если оба манипулируют переменной, что-то вроде
main Var = Var + 10 ISR Var = Var + 10
Тогда кто знает? Вы считаете, что конечным результатом будет var + 20, но если в сборке последовательность
Main - Get Var ISR - Get Var Add 10 Store Var Return Add 10 Store Var
Тогда окончательный результат будет 10, а не 20
Поскольку предыдущий плакат сказал, что для предотвращения этого вам понадобится код защиты.