Система приоритетов
Ограничения, налагаемые на код с уровнем IRQL большим или равным DISPATCHJLEVEL
IRQL dispatchjevel имеет важное значение в Windows NT. Как уже говорилось выше, Диспетчер (планировщик) Windows NT получает запросы, чтобы выполнить операцию перепланирования, на уровне IRQL dispatch_level. Этот факт имеет три важных следствия:
- Любой поток с IRQL >= DISPATCH_LEVEL не подвержен механизму планирования.
- Любой поток с IRQL >= DISPATCH_LEVEL не может использовать никакие функции ожидания Диспетчерских Объектов ядра с отличным от нуля временем ожидания.
- Любой поток с IRQL >= DISPATCH_LEVEL не должен приводить к ошибкам отсутствия страниц памяти (что происходит при обращении к участку памяти, находящемуся йа выгруженной на диск странице памяти). Иными словами, на таких уровнях IRQL может быть использована только невыгружаемая память (nonpaged pool, организация памяти будет рассмотрена в следующем разделе).
Рассмотрим эти пункты более подробно.
Так как Диспетчер выполняется на уровне IRQL DISPATCH_LEVEL, любая подпрограмма, которая выполняется на IRQL dispatchjevel или выше, не подчиняется приоритетному прерыванию (выгрузке). Таким образом, когда квант времени потока истекает, если этот поток выполняется в настоящее время на IRQL dispatch_level или выше, он продолжит выполняться, пока не попытается понизить IRQL текущего процессора ниже dispatchjevel. Это должно быть очевидно, так как исполнение на некотором уровне IRQL блокирует распознавание других событий, запрошенных на этом же или более низком уровне IRQL.
Что может быть менее очевидно, так это то, что, когда код выполняется на уровне IRQL dispatch_level или выше, он не может ждать никакие диспетчерские объекты (Dispatcher Object – см. раздел "Механизмы синхронизации"), которые еще не переведены в сигнальное состояние (состояние "свободен"). Таким образом, например, код, выполняющийся на уровне IRQL dispatch_level или выше, не может ожидать установки объектов событие или мьютекс. Так происходит потому, что действие освобождения процессора (которое происходит, когда поток переходит в режим ожидания события) требует (по крайней мере, концептуально) запуска Диспетчера. Однако, если подпрограмма выполняется на уровне dispatch_level или выше, прерывание уровня dispatchjevel (по которому запускается Диспетчер) будет маскировано и, следовательно, распознано не сразу. В результате происходит возврат обратно к коду, который вызвал операцию ожидания!
Еще менее очевидным может быть тот факт, что код, выполняющийся на уровне IRQL dispatch_level или выше не должен приводить к ошибкам отсутствия страниц (page faults). Это означает, что любой такой код сам должен быть невыгружаемым, и должен обращаться только к невыгружаемым структурам данных. В основном это объясняется тем, что код, выполняющийся на IRQL dispatch_level или выше не может ждать освобождения диспетчерского объекта. Таким образом, даже если бы страничный запрос был обработан, поток с ошибкой отсутствия страницы не мог бы быть приостановлен, пока необходимая страница читалась с диска.
DIRQLs
Все уровни IRQL выше Dispatch Level относятся к аппаратным прерываниям. Аппаратные прерывания периферийных устройств системы (например, дисков, клавиатур, последовательных портов) отображаются на уровни IRQL в диапазоне Device Level. Из Таблицы 5 вы можете видеть, что на процессорах Intel этот диапазон лежит от 3 до 26, а на машинах Alpha – от 3 до 4. Тот факт, что существует такая разница между двумя диапазонами, имеет следствием то, что NT в действительности не располагает обычные прерывания устройств в соответствии с приоритетом. Даже на процессорах Intel, где аппаратные прерывания могут иметь различные значения IRQL, назначение IRQL является случайным.
Так как это может быть важным моментом для разработчиков драйвера устройства в некоторых системах, необходимо повторить снова: связь между двумя IRQ, назначенными двум определенным устройствам не обязательно сохраняется, когда IRQL назначены этим устройствам. Назначен ли устройству с более важным IRQ более высокий (то есть более важный) уровень IRQL, полностью зависит от HAL. В действительности, в большинстве HAL стандартных многопроцессорных систем х86, для систем, которые используют архитектуры APIC, связь между IRQ и IRQL не сохраняется.
Уровни IRQL выше Device Level имеют предопределенные связи с определенными прерываниями. Profile Level относится к таймеру профилирования ядра (механизму измерения производительности системы), Clock Level относится к такту системных часов, IPI Level относится к сигналам, посылаемым от одного CPU к другому, и Power Level относится к событиям сбоя в питании.
NT резервирует, но в настоящий момент не использует IRQL High Level.
IRQL highjevel всегда определяется как самый высокий уровень IRQL в системе Windows NT. Этот уровень IRQL используется для NMI (Немаскируемого Прерывания) и других прерываний очень высокого приоритета. В редких случаях, когда драйвер устройства нуждается в блокировании прерываний на конкретном процессоре на короткий период, драйвер может поднимать IRQL до уровня high_level, но такое повышение уровня IRQL драйвером устройства считается очень решительным шагом, и в Windows NT это почти не требуется.
Подъем IRQL до уровня HIGH_LEVEL большинству драйверов Windows NT желательно никогда не делать. Блокирование прерываний является широко используемым методом для достижения синхронизации на других операционных системах (типа DOS или Win9x). Однако, в Windows NT, простое поднятие до IRQL HIGH_LEVEL в целях синхронизации не будет работать на многопроцессорных системах. Код режима ядра выполняет сериализацию, используя спин-блокировки, которые подробно описаны в разделе "Механизмы синхронизации".