Перейти к содержанию

Атомарная операция

Атомарная операция — та, которую прерывание не может застать наполовину выполненной. Чтение байта на 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 этих команд нет.

Источники

  1. Arm. ARMv7-M Architecture Reference Manual, DDI 0403E.e, 2021 — A3.5.3: однократно атомарны обращения к байту, выровненному полуслову и выровненному слову; LDRD, LDM — последовательность обращений к словам, прерванная команда выполняется заново
  2. Arm. Cortex-M3 Devices Generic User Guide, DUI 0552A, 2010 — LDREX/STREX: STREX возвращает 0 при успехе и 1 при неудаче; монитор сбрасывают CLREX, STREX и вход в исключение; функции CMSIS __LDREXW/__STREXW
  3. Arm. Cortex-M0 Devices Generic User Guide, DUI 0497A — в наборе команд Cortex-M0 нет LDREX/STREX
  4. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet, DS40002061B, 2020 — 16-битные регистры таймера читаются и пишутся через общий регистр TEMP: чтение TCNT1/ICR1 и запись любого 16-битного регистра — основная программа должна запрещать прерывания, если обработчик тоже обращается к ним

Нашли ошибку?