Рассмотрите псевдокод планировщика задач с приоритетами, содержащий блокировку ресурсов; опишите, при каких условиях возникает приоритетная инверсия, приведите конкретный пример (с временными диаграммами), и предложите практические механизмы устранения (priority inheritance, priority ceiling) с их преимуществами и недостатками
Кратко и по делу. Когда возникает приоритетная инверсия - Наличие разделяемого ресурса (мьютекс/лок) и критических секций. - Существуют как минимум три задачи с приоритетами PH>PM>PLP_H>P_M>P_LPH>PM>PL. - Низкоприоритетная задача LLL удерживает ресурс, задача высшего приоритета HHH блокируется, ожидая этот ресурс, и в этот момент задача среднего приоритета MMM становится готовой и вытесняет LLL. Итог: HHH ждёт не только из‑за LLL, но и из‑за MMM — приоритеты «инвертированы». Конкретный пример с временной диаграммой - Задачи: LLL (низкий), MMM (средний), HHH (высокий). Приоритеты PH>PM>PLP_H>P_M>P_LPH>PM>PL. - Параметры: LLL входит в критическую секцию длительностью CL=6C_L=6CL=6, MMM выполняется без блокировок CM=5C_M=5CM=5. Сценарий: - t=0t=0t=0: LLL стартует и захватывает mutex, выполняет критическую секцию. - t=1t=1t=1: HHH становится готовым и пытается захватить mutex → блокируется (ждёт LLL). - t=2t=2t=2: MMM становится готовым; так как PM>PLP_M>P_LPM>PL, MMM предшествует LLL и выполняется с t=2t=2t=2 до t=7t=7t=7. - t=7t=7t=7: MMM завершил; LLL возобновляется и завершает оставшиеся 444 единицы работы (заканчивает в t=11t=11t=11). - t=11t=11t=11: LLL освобождает mutex, HHH наконец получает ресурс и продолжает. Таблично (интервалы времени указаны в единицах): - 0 − 20\!-\!20−2: выполняется LLL (держит mutex) - 2 − 72\!-\!72−7: выполняется MMM (прерывает LLL) - 7 − 117\!-\!117−11: выполняется LLL (заканчивает CS) - 11 − ...11\!-\!...11−...: выполняется HHH Без MMMHHH получил бы mutex в t=6t=6t=6; с MMM — только в t=11t=11t=11. Это и есть приоритетная инверсия (задержка HHH увеличилась). Практические механизмы устранения 1) Priority Inheritance (наследование приоритета) - Идея: когда HHH блокируется на ресурсе, который держит LLL, временно поднять приоритет LLL до PHP_HPH. Таким образом LLL не будет вытеснен задачами со средним приоритетом и быстрее освободит ресурс. - Поведение в примере: при появлении HHH приоритет LLL повышается до PHP_HPH, MMM уже не сможет прервать LLL, LLL завершает CS в t=6t=6t=6, HHH получает ресурс — инверсия устранена. - Преимущества: - Простая и динамическая; обычно реализуется в ОС/RTOS; уменьшает время блокировки выше‑приоритетных задач. - Поддерживает транзитивное наследование (цепочки блокировок). - Недостатки: - Не даёт жёсткой верхней границы блокировки в общем случае (в сложных цепочках блокировка может суммироваться), хотя практическая задержка часто существенно меньше. - Требует отслеживания, когда и кому повышать/понижать приоритеты (сложность реализации, накладные переключения контекста). - Не предотвращает взаимные блокировки (deadlocks) — только снижает инверсию. 2) Priority Ceiling Protocol (потолок приоритета) — две реализации: Original PCP и Immediate Ceiling Protocol (ICP) - Идея: каждому ресурсу присваивается «потолок» — максимальный приоритет всех задач, которые могут его захватить: для ресурса RRR потолок = max{Pi∣ задача i может захватить R}\max\{P_i \mid\ \text{задача }i\ \text{может захватить }R\}max{Pi∣задачаiможетзахватитьR}. - Правило (ICP, более часто используемое): задача не может войти в критическую секцию (захватить ресурс), если её приоритет ниже текущего системного потолка; при захвате её приоритет немедленно повышается до потолка ресурса. - Эффект: предотвращается ситуация, в которой задача с приоритетом между владельцем ресурса и его пользователем может прервать владельца — тем самым приоритетная инверсия заранее исключается. - Преимущества: - Даёт жёсткие верхние границы блокировки (каждая задача может быть заблокирована не более чем на длину одной критической секции другой задачи в большинстве анализов RT). - Предотвращает некоторые виды дедлоков (PCP). - Подходит для жёсткого реального времени и статического анализа. - Недостатки: - Более консервативен: может ограничивать параллелизм (некоторые безопасные захваты блокируются заранее). - Требует статического знания, какие задачи могут захватить какие ресурсы (анализ и установка потолков). - Более строгая политика может привести к менее эффективному использованию CPU в динамических системах. Краткое сравнение - Priority inheritance: проще, динамическая, хорошо уменьшает инверсии в типичных сценариях; может не дать жёсткой гарантии времени блокировки в сложных системах. - Priority ceiling: даёт формальные границы блокировки и предотвращает некоторые дедлоки; требует преданализa и может быть избыточно ограничивающим. Рекомендация практического применения - Для общих RTOS и систем со многими динамическими ресурсами часто используют priority inheritance как компромисс (простая реализация в мьютексах). - Для жёсткого реального времени и приёмистых (safety‑critical) систем предпочитают priority ceiling (PCP/ICP) с предварительным анализом, чтобы иметь математически обоснованные задержки.
Когда возникает приоритетная инверсия
- Наличие разделяемого ресурса (мьютекс/лок) и критических секций.
- Существуют как минимум три задачи с приоритетами PH>PM>PLP_H>P_M>P_LPH >PM >PL .
- Низкоприоритетная задача LLL удерживает ресурс, задача высшего приоритета HHH блокируется, ожидая этот ресурс, и в этот момент задача среднего приоритета MMM становится готовой и вытесняет LLL.
Итог: HHH ждёт не только из‑за LLL, но и из‑за MMM — приоритеты «инвертированы».
Конкретный пример с временной диаграммой
- Задачи: LLL (низкий), MMM (средний), HHH (высокий). Приоритеты PH>PM>PLP_H>P_M>P_LPH >PM >PL .
- Параметры: LLL входит в критическую секцию длительностью CL=6C_L=6CL =6, MMM выполняется без блокировок CM=5C_M=5CM =5.
Сценарий:
- t=0t=0t=0: LLL стартует и захватывает mutex, выполняет критическую секцию.
- t=1t=1t=1: HHH становится готовым и пытается захватить mutex → блокируется (ждёт LLL).
- t=2t=2t=2: MMM становится готовым; так как PM>PLP_M>P_LPM >PL , MMM предшествует LLL и выполняется с t=2t=2t=2 до t=7t=7t=7.
- t=7t=7t=7: MMM завершил; LLL возобновляется и завершает оставшиеся 444 единицы работы (заканчивает в t=11t=11t=11).
- t=11t=11t=11: LLL освобождает mutex, HHH наконец получает ресурс и продолжает.
Таблично (интервалы времени указаны в единицах):
- 0 − 20\!-\!20−2: выполняется LLL (держит mutex)
- 2 − 72\!-\!72−7: выполняется MMM (прерывает LLL)
- 7 − 117\!-\!117−11: выполняется LLL (заканчивает CS)
- 11 − ...11\!-\!...11−...: выполняется HHH
Без MMM HHH получил бы mutex в t=6t=6t=6; с MMM — только в t=11t=11t=11. Это и есть приоритетная инверсия (задержка HHH увеличилась).
Практические механизмы устранения
1) Priority Inheritance (наследование приоритета)
- Идея: когда HHH блокируется на ресурсе, который держит LLL, временно поднять приоритет LLL до PHP_HPH . Таким образом LLL не будет вытеснен задачами со средним приоритетом и быстрее освободит ресурс.
- Поведение в примере: при появлении HHH приоритет LLL повышается до PHP_HPH , MMM уже не сможет прервать LLL, LLL завершает CS в t=6t=6t=6, HHH получает ресурс — инверсия устранена.
- Преимущества:
- Простая и динамическая; обычно реализуется в ОС/RTOS; уменьшает время блокировки выше‑приоритетных задач.
- Поддерживает транзитивное наследование (цепочки блокировок).
- Недостатки:
- Не даёт жёсткой верхней границы блокировки в общем случае (в сложных цепочках блокировка может суммироваться), хотя практическая задержка часто существенно меньше.
- Требует отслеживания, когда и кому повышать/понижать приоритеты (сложность реализации, накладные переключения контекста).
- Не предотвращает взаимные блокировки (deadlocks) — только снижает инверсию.
2) Priority Ceiling Protocol (потолок приоритета) — две реализации: Original PCP и Immediate Ceiling Protocol (ICP)
- Идея: каждому ресурсу присваивается «потолок» — максимальный приоритет всех задач, которые могут его захватить: для ресурса RRR потолок = max{Pi∣ задача i может захватить R}\max\{P_i \mid\ \text{задача }i\ \text{может захватить }R\}max{Pi ∣ задача i может захватить R}.
- Правило (ICP, более часто используемое): задача не может войти в критическую секцию (захватить ресурс), если её приоритет ниже текущего системного потолка; при захвате её приоритет немедленно повышается до потолка ресурса.
- Эффект: предотвращается ситуация, в которой задача с приоритетом между владельцем ресурса и его пользователем может прервать владельца — тем самым приоритетная инверсия заранее исключается.
- Преимущества:
- Даёт жёсткие верхние границы блокировки (каждая задача может быть заблокирована не более чем на длину одной критической секции другой задачи в большинстве анализов RT).
- Предотвращает некоторые виды дедлоков (PCP).
- Подходит для жёсткого реального времени и статического анализа.
- Недостатки:
- Более консервативен: может ограничивать параллелизм (некоторые безопасные захваты блокируются заранее).
- Требует статического знания, какие задачи могут захватить какие ресурсы (анализ и установка потолков).
- Более строгая политика может привести к менее эффективному использованию CPU в динамических системах.
Краткое сравнение
- Priority inheritance: проще, динамическая, хорошо уменьшает инверсии в типичных сценариях; может не дать жёсткой гарантии времени блокировки в сложных системах.
- Priority ceiling: даёт формальные границы блокировки и предотвращает некоторые дедлоки; требует преданализa и может быть избыточно ограничивающим.
Рекомендация практического применения
- Для общих RTOS и систем со многими динамическими ресурсами часто используют priority inheritance как компромисс (простая реализация в мьютексах).
- Для жёсткого реального времени и приёмистых (safety‑critical) систем предпочитают priority ceiling (PCP/ICP) с предварительным анализом, чтобы иметь математически обоснованные задержки.