Инверсия приоритетов и наследование приоритета
В системе с приоритетами задача с высоким приоритетом должна выполняться первой. Но если ей нужен мьютекс, который держит задача с низким приоритетом, она вынуждена ждать. А если в этот момент проснётся задача со средним приоритетом, ожидание может затянуться неограниченно.
Обновлено Редакция GAW
Инверсия приоритетов (priority inversion) — ситуация в многозадачной системе, когда задача с высоким приоритетом ждёт задачу с низким. Причина — общий ресурс: низкоприоритетная задача взяла мьютекс, и высокоприоритетной, которой нужен тот же ресурс, остаётся ждать его освобождения.
Сценарий
| Шаг | Что происходит |
|---|---|
| 1 | Задача L (низкий приоритет) берёт мьютекс |
| 2 | Просыпается задача H (высокий приоритет), вытесняет L и пытается взять мьютекс — блокируется |
| 3 | L продолжает работу, чтобы отдать мьютекс, — это ограниченная инверсия |
| 4 | Просыпается задача M (средний приоритет) и вытесняет L |
| 5 | H ждёт L, которая ждёт M, — неограниченная инверсия |
Руководство FreeRTOS называет шаги 4–5 худшим сценарием: задача M может блокировать и L, и H сколько угодно долго, хотя к ресурсу отношения не имеет.
Наследование приоритета
Мьютекс во FreeRTOS временно поднимает приоритет держателя до приоритета самой приоритетной из ожидающих задач. В сценарии выше L получает приоритет H, и M больше не может её вытеснить. Когда L отдаёт мьютекс, её приоритет возвращается к исходному.
Ограничения, о которых предупреждает FreeRTOS:
- наследование ограничивает инверсию временем удержания мьютекса, но не устраняет её;
- оно усложняет анализ времени, и полагаться на него для корректности — плохая практика;
- реализация упрощена: задача, держащая несколько мьютексов, остаётся на наивысшем унаследованном приоритете, пока не вернёт их все.
У двоичного семафора наследования нет, поэтому для защиты ресурса он хуже мьютекса.
Как избежать
FreeRTOS советует думать об этом на этапе проектирования, продумывая, как задачи обращаются к ресурсам. Чем короче мьютекс удерживается, тем короче и ограниченная инверсия. Радикальный способ из того же руководства — задача-привратник (gatekeeper): она единолично владеет ресурсом, а остальные задачи посылают ей запросы через очередь. Так взаимное исключение получается без риска инверсии и взаимной блокировки. Другая опасность при нескольких мьютексах — взаимная блокировка. Общие вопросы разделения данных — в статье «Прерывания и общие данные».
Частые вопросы
Почему инверсию называют неограниченной?
Пока задача с низким приоритетом держит мьютекс, задача со средним приоритетом может вытеснять её сколько угодно долго. Высокоприоритетная задача ждёт низкоприоритетную, а та даже не выполняется. Время ожидания определяет не код, держащий мьютекс, а посторонняя задача, поэтому его нельзя оценить заранее.
Решает ли наследование приоритета проблему полностью?
Нет. Руководство FreeRTOS прямо пишет, что наследование не устраняет инверсию, а только ограничивает её временем, пока низкоприоритетная задача держит мьютекс. Оно усложняет анализ времени, и полагаться на него для правильной работы системы — плохая практика. Лучше держать мьютексы как можно короче.
Есть ли наследование приоритета у семафора?
Во FreeRTOS — нет: semphr.h указывает, что двоичный семафор его не использует. Поэтому для защиты общего ресурса берут мьютекс, а двоичный семафор оставляют для синхронизации с прерываниями и событиями.
Источники
- 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.2: задержку высокоприоритетной задачи низкоприоритетной называют инверсией приоритетов; если при этом выполняется задача среднего приоритета, высокоприоритетная ждёт низкоприоритетную, которая даже не может выполняться, — неограниченная инверсия (рис. 8.4); в небольших системах её часто можно избежать на этапе проектирования; 8.3.3: наследование приоритета не устраняет инверсию, а ограничивает её во времени: приоритет держателя мьютекса временно поднимается до приоритета самой приоритетной ожидающей задачи и восстанавливается при возврате мьютекса; усложняет анализ времени, полагаться на него для корректности — плохая практика; реализация FreeRTOS упрощённая: задача остаётся на наивысшем унаследованном приоритете, пока не вернёт все мьютексы; разд. 8.4: задача-привратник единолично владеет ресурсом, остальные обращаются к ней через очередь — взаимное исключение без риска инверсии приоритетов и тупика
- FreeRTOS Kernel V11.1.0, include/semphr.h (GitHub FreeRTOS/FreeRTOS-Kernel) — двоичный семафор не использует наследование приоритета, для альтернативы с наследованием — xSemaphoreCreateMutex(); мьютексы используют наследование приоритета и не могут применяться в прерываниях
Нашли ошибку?
Смотрите также
- Мьютекс (mutex)Объект RTOS для взаимного исключения задач при доступе к общему ресурсу
- Семафор (двоичный и счётный)Объект RTOS для сигнала о событиях и учёта свободных ресурсов
- Взаимная блокировка (deadlock)Задачи бесконечно ждут ресурсы, которые держат друг у друга
- Задержка прерыванияВремя от запроса до обработчика: Cortex-M3 и AVR
- Прерывания и общие данныеISR на AVR и Cortex-M, атомарность, критические секции, кольцевой буфер