Мьютекс (mutex) в RTOS
Когда две задачи RTOS пишут в один UART или в одну структуру данных, нужен способ пускать к ресурсу только одну из них. Для этого служит мьютекс — «жетон», без которого к ресурсу не подходят. Разбираем, как он работает во FreeRTOS и чем отличается от семафора.
Обновлено Редакция GAW
Мьютекс (mutex, от mutual exclusion — взаимное исключение) — объект операционной системы реального времени, который пропускает к общему ресурсу только одну задачу. Задача «берёт» мьютекс перед обращением к ресурсу и «отдаёт» его после. Пока мьютекс занят, другие задачи, пытающиеся его взять, ждут в заблокированном состоянии.
Как это выглядит во FreeRTOS
SemaphoreHandle_t uart_mutex;
void app_init(void) {
uart_mutex = xSemaphoreCreateMutex(); /* before the scheduler starts */
}
void log_line(const char *s) {
if (xSemaphoreTake(uart_mutex, pdMS_TO_TICKS(50)) == pdTRUE) {
uart_write(s); /* only one task at a time */
xSemaphoreGive(uart_mutex); /* always give it back */
} else {
/* timeout: likely a design error, e.g. a deadlock */
}
}
Руководство FreeRTOS подчёркивает, что механизм держится только на дисциплине: ничто не мешает задаче обратиться к ресурсу без мьютекса, но все задачи «договариваются» так не делать.
Мьютекс и семафор
| Мьютекс | Двоичный семафор | |
|---|---|---|
| Назначение | защита общего ресурса | синхронизация с событием |
| Кто отдаёт | та же задача, что взяла | обычно прерывание или другая задача |
| Наследование приоритета | есть | нет |
| Из прерывания | нельзя | можно (FromISR-функции) |
Наследование приоритета нужно против инверсии приоритетов: если мьютекс держит низкоприоритетная задача, её приоритет временно поднимается до приоритета ждущей. Вторая опасность при работе с несколькими мьютексами — взаимная блокировка.
Когда мьютекс не нужен
Для обмена данными между прерыванием и основным циклом без RTOS мьютекс бесполезен: прерывание не может ждать. Там используют критическую секцию — короткий запрет прерываний. Общие приёмы борьбы с состоянием гонки — в статье «Прерывания и общие данные».
Частые вопросы
Чем мьютекс отличается от двоичного семафора?
Устроены они почти одинаково, а назначение разное. Мьютекс защищает ресурс: его берёт и обязательно отдаёт одна и та же задача. Двоичный семафор синхронизирует: обычно его отдаёт прерывание или другая задача, а взявшая задача обратно не возвращает. Во FreeRTOS у мьютекса есть наследование приоритета, у семафора — нет.
Можно ли взять мьютекс в обработчике прерывания?
Во FreeRTOS — нет: semphr.h прямо запрещает это. Наследование приоритета имеет смысл только для задач, а прерывание не может ждать освобождения мьютекса. Из прерывания работают с двоичным семафором или уведомлением задачи, а сам ресурс обрабатывают в задаче.
Что будет, если задача возьмёт мьютекс и не отдаст?
Все остальные задачи, которым нужен ресурс, будут ждать вечно или до таймаута. Поэтому мьютекс отдают на всех путях выхода из функции, а ждут его с таймаутом, немного большим ожидаемого времени удержания. По руководству FreeRTOS неудача по таймауту — признак ошибки проектирования, например взаимной блокировки.
Источники
- R. Barry, FreeRTOS Team. Mastering the FreeRTOS Real Time Kernel — A Hands-On Tutorial Guide, Release 1.1.0 (Amazon Web Services, 2023) — разд. 8.3: мьютекс — особый двоичный семафор для управления доступом к ресурсу, общему для нескольких задач (MUTual EXclusion); включается configUSE_MUTEXES; задача берёт жетон перед доступом и обязана его вернуть; семафор для взаимного исключения всегда возвращают, для синхронизации — обычно нет; механизм работает только на дисциплине программиста; 8.3.3: в отличие от двоичного семафора у мьютекса есть наследование приоритета, поэтому мьютексы нельзя использовать в прерываниях; 8.3.4: тупик; 8.3.5: рекурсивные мьютексы
- FreeRTOS Kernel V11.1.0, include/semphr.h (GitHub FreeRTOS/FreeRTOS-Kernel) — xSemaphoreCreateMutex(): мьютекс берут и отдают макросами xSemaphoreTake() и xSemaphoreGive(), рекурсивные макросы с ним использовать нельзя; семафор этого типа использует наследование приоритета, поэтому взявшая его задача ОБЯЗАНА его вернуть; мьютексы нельзя использовать в обработчиках прерываний; xSemaphoreCreateMutexStatic() — без динамической памяти
Нашли ошибку?
Смотрите также
- Семафор (двоичный и счётный)Объект RTOS для сигнала о событиях и учёта свободных ресурсов
- Инверсия приоритетовЗадача с высоким приоритетом ждёт задачу с низким из-за общего ресурса
- Взаимная блокировка (deadlock)Задачи бесконечно ждут ресурсы, которые держат друг у друга
- Критическая секцияУчасток кода под запретом прерываний
- Состояние гонкиОшибка, зависящая от момента срабатывания прерывания
- Прерывания и общие данныеISR на AVR и Cortex-M, атомарность, критические секции, кольцевой буфер