Атомарная операция
Атомарная операция — та, которую прерывание не может застать наполовину выполненной. Чтение байта на AVR или выровненного слова на Cortex-M атомарно само по себе, а вот 16-битное чтение на AVR или увеличение переменной в памяти — нет.
Обновлено Редакция GAW
Атомарная операция (atomic operation) — операция, которая с точки зрения прерываний и других потоков выполняется целиком: её нельзя застать на середине. Важна она там, где основная программа и обработчик прерывания пользуются одними данными, иначе возникает состояние гонки.
Что атомарно само по себе
| Ядро | Атомарно | Не атомарно |
|---|---|---|
| AVR (8 бит) | чтение и запись байта | 16- и 32-битные переменные, чтение-изменение-запись |
| Cortex-M | байт, выровненное полуслово, выровненное слово | LDRD, LDM, невыровненные данные, i++ |
Для Cortex-M это раздел A3.5.3 руководства ARMv7-M: команды LDRD и LDM — последовательность обращений, и прерванная команда выполняется заново. Операция «прочитать, изменить, записать», как counter++, атомарной не бывает ни на одном ядре без специальных средств.
Особый случай — 16-битные регистры таймера ATmega: старший байт идёт через общий регистр TEMP, и по даташиту Microchip основная программа должна запрещать прерывания при таких обращениях, если обработчик тоже читает или пишет 16-битные регистры.
Как сделать операцию атомарной
- Запрет прерываний — критическая секция: на AVR
ATOMIC_BLOCK, на Cortex-M — PRIMASK или BASEPRI. - LDREX/STREX на Cortex-M3 и старше: STREX запишет значение, только если с момента LDREX никто не вмешался, и вернёт 0; иначе вернёт 1, и цикл повторяют. Вход в исключение снимает метку, поэтому вклинившееся прерывание просто заставит повторить попытку. У Cortex-M0 этих команд нет.
- Bit-banding у Cortex-M3/M4 — изменение одного бита одной записью, см. бит-бэндинг.
Не путать атомарность с volatile: volatile заставляет компилятор каждый раз обращаться к памяти, но не делает 16-битное чтение на AVR неделимым. Подробный разбор с примерами — в статье «Прерывания и общие данные», маски и операции с битами регистров — в калькуляторе битовых масок.
Частые вопросы
Атомарно ли чтение 16-битной переменной на AVR?
Нет: AVR 8-битный и читает 16-битную переменную двумя командами. Если между ними обработчик изменит переменную, получится смесь старого и нового значения. Такое чтение заключают в ATOMIC_BLOCK или копируют переменную при запрещённых прерываниях.
Атомарно ли i++ на Cortex-M?
Нет. Чтение и запись выровненного 32-битного слова по отдельности атомарны, но i++ — это чтение, сложение и запись, и прерывание может вклиниться между ними. Для неделимого изменения используют LDREX/STREX с повтором при неудаче или запрет прерываний.
Как работают LDREX и STREX?
LDREX читает слово и помечает адрес как эксклюзивный. STREX записывает новое значение, только если с момента LDREX метку не сняли, и возвращает 0 при успехе или 1 при неудаче — тогда цикл повторяют. Метку снимает, в частности, вход в любое исключение, поэтому вклинившееся прерывание заставит повторить операцию. У Cortex-M0 этих команд нет.
Источники
- Arm. ARMv7-M Architecture Reference Manual, DDI 0403E.e, 2021 — A3.5.3: однократно атомарны обращения к байту, выровненному полуслову и выровненному слову; LDRD, LDM — последовательность обращений к словам, прерванная команда выполняется заново
- Arm. Cortex-M3 Devices Generic User Guide, DUI 0552A, 2010 — LDREX/STREX: STREX возвращает 0 при успехе и 1 при неудаче; монитор сбрасывают CLREX, STREX и вход в исключение; функции CMSIS __LDREXW/__STREXW
- Arm. Cortex-M0 Devices Generic User Guide, DUI 0497A — в наборе команд Cortex-M0 нет LDREX/STREX
- Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet, DS40002061B, 2020 — 16-битные регистры таймера читаются и пишутся через общий регистр TEMP: чтение TCNT1/ICR1 и запись любого 16-битного регистра — основная программа должна запрещать прерывания, если обработчик тоже обращается к ним
Нашли ошибку?