Как используются IRQL
NT управляет прерываниями путем отображения уровней прерывания контроллера
прерываний в собственную аппаратно-независимую таблицу уровней прерываний.
Осуществляет отображение слой абстрагирования от оборудования (HAL - модуль
NT, специально написанный для конкретных контроллеров прерываний, материнских
плат, либо наборов микросхем процессоров). В мультипроцессорных системах
принять прерывание может любой процессор, поэтому NT поддерживает независимый
IRQL для каждого процессора. IRQL процессора представляет уровень
прерывания, который маскируется в данный момент процессором и прямо соответствует
(аналогичен) прерываниям, которые маскирует контроллер прерываний CPU.
Поскольку уровни IRQL NT не привязаны к какой-либо спецификации оборудования,
NT также может отображать в свою иерархию приоритетов неаппаратные типы
прерываний. Операционная система использует программные прерывания в основном
для запуска операций планирования, таких как переключение потоков или
обработка завершения ввода/вывода.
Когда NT обслуживает аппаратное прерывание, NT устанавливает IRQL процессора
в соответствующее значение таблицы IRQL NT. NT программирует контроллер
прерываний так, чтобы он маскировал прерывания с более низким приоритетом,
и драйверы устройств (так же как и NT) могут запрашивать IRQL для определения его значения. (Как мы увидим позднее, NT позволяет выполнять некоторые операции только когда IRQL меньше определенных значений.)
Размер таблицы IRQL разнится между архитектурами процессоров (Intel, Alpha и др.) для того, чтобы лучше отображать уровни прерываний, предоставляемые контроллерами прерываний, однако уровни прерываний, которые могут найти интересными разработчики драйверов устройств и разработчики NT, имеют символические имена. Таблица 5 представляет символические имена IRQL и соответствующие им числовые значения в архитектурах Intel и Alpha.
Низшие уровни IRQL (от passive_level до dispatch_level) используются для синхронизации программных частей операционной системы. Эти IRQL сконструированы как программные прерывания. Уровни IRQL выше dispatch_level, имеют ли они конкретные мнемонические имена или нет, отражают приоритеты аппаратных прерываний. Таким образом, эти аппаратные IRQL часто упоминаются как уровни IRQL Устройства (или DIRQL).
Конкретные значения, назначенные мнемоническим именам IRQL, изменяются от системы к системе. Взаимоотношения между программными уровнями IRQL от системы к системе остаются постоянными; верным также остается положение о том, что программные уровни IRQL имеют более низкий приоритет, чем аппаратные IRQL. Таким образом, IRQL passive_level всегда является самым низким уровнем IRQL в системе, apcjevel всегда выше, чем passive_level, и dispatch_level всегда выше, чем apc_level. Все эти уровни IRQL всегда ниже, чем самый низкий уровень DIRQL.
Таблица 5. Символические и числовые определения IRQL
Символическое
имя
|
Предназначение
|
Уровень Intel |
Уровень Alpha |
HIGH LEVEL
|
Наивысший
уровень прерывания
|
31 |
7 |
POWER LEVEL
|
Power event
|
30 |
7 |
IPI LEVEL
|
Межпроцессорный
сигнал
|
29 |
6 |
CLOCK LEVEL
|
такт системных
часов
|
28 |
5 |
PROFILE
LEVEL
|
Контроль
производительности
|
27 |
3 |
DEVICE
LEVEL
|
Обычные
прерывания устройств
|
3-26 |
3-4 |
DISPATCH_LEVEL
|
Операции
планирования и отложенные вызовы процедур (DPC)
|
2 |
2 |
APC LEVEL
|
Асинхронные
вызовы процедур (АРС)
|
1 |
1 |
PASSIVE
LEVEL
|
Нет прерываний
|
0 |
0 |
В отличие от программных IRQL, значения и отношения аппаратных IRQL
могут изменяться в зависимости от реализации аппаратной части системы.
Например, в архитектурах на основе х86, уровень IRQL profile_level ниже,
чем IRQL ipi_level, который является в свою очередь ниже, чем IRQL power_level.
Однако, на MIPS системах, IRQL power_level и IRQL ipi_level имеют то же
самое значение, и оба ниже, чем IRQL profilejevel.
Уровни IRQL являются главным методом, используемым для расположения по
приоритетам действий операционной системы Windows NT. Повышение уровня
IRQL позволяет подпрограмме операционной системы как управлять повторной
входимос-тью (реентерабельность) так и гарантировать, что она может продолжать
работу без приоритетного прерывания (вытеснения) некоторыми другими действиями.
Следующие разделы описывают, как используются наиболее распространенные
уровни IRQL.
IRQL PASSIVE_LEVEL, APC_LEVEL и DISPATCH_LEVEL
Наименьший приоритет IRQL в таблице 5 - Passive Level. Этот уровень
является обычным уровнем IRQL, на котором производится работа в операционной
системе, как в пользовательском режиме, так и в режиме ядра. Когда процессор
находится в этом состоянии, не происходит никакой Деятельности по обработке
прерываний. Подпрограмма, выполняющаяся на уровне IRQL passive_level,
может быть подвергнута прерыванию и вытеснению почти всем, чем угодно
еще случившемся в системе. Так, потоки, выполняющиеся на IRQL passive_level,
подвергаются вытеснению Диспетчером (планировщиком) по истечении их кванта
времени.
Большинство подпрограмм уровня исполнительной системы Windows NT (то есть
подпрограммы режима ядра, не принадлежащие Микроядру и HAL) стремятся
держать уровень IRQL как можно более низким. В большинстве случаев, это
приводит к выполнению большинства подпрограмм на уровне IRQL passive_level.
Эта стратегия повышает возможность выполнения действий с высоким уровнем
IRQL.
Следующие два уровня IRQL выше Passive Level (APC Level и Dispatch Level)
-программные уровни прерываний, связанные с планировщиком.
Когда система находится на уровне APC Level,
исполняющийся поток не будет получать запросы АРС, которые NT обычно использует
для операций завершения ввода/вывода.
IRQL dispatch_level используется внутри Windows NT для двух различных действий:
- Обработка Отложенных Вызовов Процедур (DPCs);
- Выполнение Диспетчера (планировщик NT).
DPC обработка обсуждена позже в этой главе, в собственном разделе. Следовательно,
мы ограничимся обсуждением Диспетчера. Диспетчер, является планировщиком
потоков Windows NT. Он отвечает за реализацию алгоритма планирования,
который выбирает, какой поток будет выполняться, и осуществляет приоритетное
прерывание (выгрузку) в конце кванта времени.
Диспетчер (планировщик) Windows NT получает запросы, чтобы выполнить операцию
перепланирования на уровне IRQL dispatch_level. Когда операционная система
решает заменить поток, который выполняется на текущем процессоре, она
иногда может вызывать Диспетчер напрямую. Однако, когда система выполняется
на уровне IRQL выше, чем dispatchjevel, она запрашивает программное прерывание
уровня dispatch_level. Результатом явится запуск на текущем процессоре
Диспетчера в следующий раз, когда уровень dispatch_level станет наиболее
приоритетным для обслуживания системой.
Рассмотрим, например, случай потока, выполняющегося в режиме пользователя.
Так как он выполняется в режиме пользователя, этот поток, конечно, выполняется
на IRQL passive_level. В то время как поток
выполняется, часы периодически генерируют прерывания, чтобы указать операционной
системе прохождение промежутка времени. С каждым переданным тиком часов,
программа обработки прерывания часов уменьшает остающийся у выполняющегося
в данный момент потока квант времени. Когда оставшийся у потока квант
уменьшается до нуля, программа обработки прерывания часов генерирует прерывание
уровня dispatchjevel, чтобы запросить запуск Диспетчера и выбор им следующего
потока для выполнения. Так как программа обработки прерывания часов выполняется
на уровне IRQL большем, чем dispateh_level (она выполняется на уровне
IRQL CLOCK2_LEVEL на х86 процессорах), обработка запроса для Диспетчера
откладывается.
После генерирования прерывания уровня dispatch_level, программа обработки
прерывания часов заканчивает любую другую работу, которую она должна сделать
и управление возвращается Микроядру.
Затем Микроядро распознает следующее самое высоко приоритетное прерывание,
которое находится в режиме ожидания. Каждое прерывание обслуживается по
очереди. Когда для обслуживания не остается никаких прерываний уровня
выше dispatchjevel, выполняется программа обработки прерывания уровня
dispatch_level. Эта программа обработки прерывания обрабатывает список
DPC (обсуждаемый позже), и задействует Диспетчер, чтобы выбрать новый
поток выполнения.
Когда задействуется Диспетчер, он обращает внимание, что квант времени
текущего потока был уменьшен до нуля. Затем Диспетчер осуществляет алгоритм
планирования Windows NT, чтобы определить следующий поток, который нужно
запланировать. Если выбран новый поток (мог бы быть перепланирован предыдущий
поток), происходит переключение контекста. Если нет никаких ожидающих
вызовов АРС для вновь выбранного потока, код потока будет выполнен, когда
система возвратится обратно к уровню IRQL PASSIVE_LEVEL.
Ограничения, налагаемые на код с уровнем 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
в целях синхронизации не будет работать на многопроцессорных системах.
Код режима ядра выполняет сериализацию, используя спин-блокировки, которые
подробно описаны в разделе "Механизмы синхронизации".