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

Инверсия приоритетов и наследование приоритета

В системе с приоритетами задача с высоким приоритетом должна выполняться первой. Но если ей нужен мьютекс, который держит задача с низким приоритетом, она вынуждена ждать. А если в этот момент проснётся задача со средним приоритетом, ожидание может затянуться неограниченно.

Обновлено Редакция GAW

Инверсия приоритетов (priority inversion) — ситуация в многозадачной системе, когда задача с высоким приоритетом ждёт задачу с низким. Причина — общий ресурс: низкоприоритетная задача взяла мьютекс, и высокоприоритетной, которой нужен тот же ресурс, остаётся ждать его освобождения.

Сценарий

ШагЧто происходит
1Задача L (низкий приоритет) берёт мьютекс
2Просыпается задача H (высокий приоритет), вытесняет L и пытается взять мьютекс — блокируется
3L продолжает работу, чтобы отдать мьютекс, — это ограниченная инверсия
4Просыпается задача M (средний приоритет) и вытесняет L
5H ждёт L, которая ждёт M, — неограниченная инверсия

Руководство FreeRTOS называет шаги 4–5 худшим сценарием: задача M может блокировать и L, и H сколько угодно долго, хотя к ресурсу отношения не имеет.

Наследование приоритета

Мьютекс во FreeRTOS временно поднимает приоритет держателя до приоритета самой приоритетной из ожидающих задач. В сценарии выше L получает приоритет H, и M больше не может её вытеснить. Когда L отдаёт мьютекс, её приоритет возвращается к исходному.

Ограничения, о которых предупреждает FreeRTOS:

  • наследование ограничивает инверсию временем удержания мьютекса, но не устраняет её;
  • оно усложняет анализ времени, и полагаться на него для корректности — плохая практика;
  • реализация упрощена: задача, держащая несколько мьютексов, остаётся на наивысшем унаследованном приоритете, пока не вернёт их все.

У двоичного семафора наследования нет, поэтому для защиты ресурса он хуже мьютекса.

Как избежать

FreeRTOS советует думать об этом на этапе проектирования, продумывая, как задачи обращаются к ресурсам. Чем короче мьютекс удерживается, тем короче и ограниченная инверсия. Радикальный способ из того же руководства — задача-привратник (gatekeeper): она единолично владеет ресурсом, а остальные задачи посылают ей запросы через очередь. Так взаимное исключение получается без риска инверсии и взаимной блокировки. Другая опасность при нескольких мьютексах — взаимная блокировка. Общие вопросы разделения данных — в статье «Прерывания и общие данные».

Частые вопросы

Почему инверсию называют неограниченной?

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

Решает ли наследование приоритета проблему полностью?

Нет. Руководство FreeRTOS прямо пишет, что наследование не устраняет инверсию, а только ограничивает её временем, пока низкоприоритетная задача держит мьютекс. Оно усложняет анализ времени, и полагаться на него для правильной работы системы — плохая практика. Лучше держать мьютексы как можно короче.

Есть ли наследование приоритета у семафора?

Во FreeRTOS — нет: semphr.h указывает, что двоичный семафор его не использует. Поэтому для защиты общего ресурса берут мьютекс, а двоичный семафор оставляют для синхронизации с прерываниями и событиями.

Источники

  1. 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: задача-привратник единолично владеет ресурсом, остальные обращаются к ней через очередь — взаимное исключение без риска инверсии приоритетов и тупика
  2. FreeRTOS Kernel V11.1.0, include/semphr.h (GitHub FreeRTOS/FreeRTOS-Kernel) — двоичный семафор не использует наследование приоритета, для альтернативы с наследованием — xSemaphoreCreateMutex(); мьютексы используют наследование приоритета и не могут применяться в прерываниях

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