AG
Все записи

#️⃣4️⃣ Race Condition (состояние гонки) в embedded системах

2 min read
Embeddedrace conditionсинхронизация

#️⃣4️⃣** Race Condition (состояние гонки) в embedded системах
Это ситуация, когда два или более асинхронно выполняющихся фрагмента кода (например, прерывание и фоновый цикл) одновременно получают доступ к общему ресурсу и пытаются его изменить.
При этом результат зависит от времени или порядка выполнения операций.

Причины возникновения
✔️ Отсутствие синхронизации между фрагментами кода, которые обращаются к общему ресурсу.
✔️ Частое использование прерываний - они могут вызывать race condition из-за взаимодействия между задачами приложения и обработчиками прерываний.
✔️ Оптимизация компилятора, которая может менять порядок операций, даже если исходный код выглядит безопасным.

Race condition может привести к непредсказуемому поведению системы. Вот некоторые возможные последствия:
порча данных,
уязвимости,
взаимные блокировки.

ℹ️ Как обнаружить
🔸 Проверка кода и подключение статического анализа.
Изучи кодовую базу, выпиши критические участки, где осуществляется доступ к общим ресурсам или их изменение. Ищи шаблоны, в которых состояние считывается и записывается без надлежащих механизмов синхронизации. Подключи уже наконец clang-tidy или аналог статического анализатора!

🔸 Ведение логов и отслеживание.
Добавь подробное ведение журнала логов для критических участков кода. Изучая логи, можно определить порядок выполнения и выявить аномалии, указывающие на состояние гонки.

🔸 Динамический анализ и стресс-тестирование.
Проводи стресс-тесты, имитирующие сценарии с высокой степенью параллелизма, чтобы выявить условия гонки.

🔸 Проверка блокировки оптимизаций компилятора.
На всех ли критических несинхронизируемых ресурсах стоит модификатор volatile?

Как поправить
Используй атомарные операции, чтобы избежать промежуточных состояний. Можно блокировать прерывания для bare-metal или применять std::atomic из стандартной библиотеки.

Блокировки или мьютексы. Обеспечь взаимное исключение при доступе к общим ресурсам средствами объектов синхронизации операционной системы.

Используй неизменяемые структуры данных. Неизменяемый формат класса в C++ достигается с помощью ключевого слова const. Оно позволяет объявить переменные-члены и методы класса как неизменяемые.

Применяй транзакционную память. убедись, что группы операций выполняются транзакционно. У меня для этого есть простой паттерн.

Избегай общего состояния, используя передачу сообщений для взаимодействия задач.

Юзай команды для управления параллелизмом более низкого уровня.

Применяй такие инструкции, как барьеры и защелки. В embedded-системах барьеры часто реализуются через инструкции памяти:
DMB (Data Memory Barrier) - гарантирует выполнение всех предыдущих операций записи перед продолжением.
DSB (Data Synchronization Barrier) - блокирует выполнение до завершения всех операций с памятью.
ISB (Instruction Synchronization Barrier) - очищает кэш инструкций, что важно при самомодифицируемом коде.

А вы часто сталкивались с такими случаями? Делитесь в комментариях, как решали!
#IT #embedded #кейсы