Управление приоритетами оборудования

IrQL, в котором выполняется подпрограмма драйвера, определяет, какие драйверы в режиме ядра поддерживают подпрограммы, которые он может вызывать. Например, некоторые подпрограммы поддержки драйверов требуют, чтобы вызывающий объект работал в IRQL = DISPATCH_LEVEL. Другие не могут вызываться безопасно, если вызывающий объект выполняется в любом irQL выше PASSIVE_LEVEL.

Ниже приведен список списков IRQL, в которых вызываются наиболее часто реализуемые стандартные подпрограммы драйвера. Списки IRQL отображаются от самого низкого до самого высокого приоритета.

PASSIVE_LEVEL
Прерывания в маске отключены — нет.

Подпрограммы драйвера, вызываемая по адресу PASSIVE_LEVEL — DriverEntry, AddDevice, Reinitialize, Unload подпрограммы, большинство подпрограмм диспетчеризации, потоки, созданные драйвером, обратные вызовы рабочих потоков.

APC_LEVEL
Прерывания в маске отключены — APC_LEVEL прерывания маскируются.

Подпрограммы драйвера, вызываемая по адресу APC_LEVEL — некоторые подпрограммы диспетчеризации (см. раздел Подпрограммы диспетчеризации и списки IRQ).

DISPATCH_LEVEL
Прерывания в маске отключены — DISPATCH_LEVEL и APC_LEVEL прерывания маскируются. Могут возникать прерывания устройства, часов и сбоя питания.

Подпрограммы драйвера, вызываемая по адресу DISPATCH_LEVEL — StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (с удержанием блокировки спина отмены), DpcForIsr, CustomTimerDpc, CustomDpc .

DIRQL
Прерывания masked Off — все прерывания в IRQL <= DIRQL объекта прерывания драйвера. Могут возникать прерывания устройства с более высоким значением DIRQL, а также прерывания часов и сбоев питания.

Подпрограммы драйвера, вызываемая в DIRQL— подпрограммы InterruptService, SynchCritSection .

Единственное различие между APC_LEVEL и PASSIVE_LEVEL заключается в том, что процесс, выполняемый в APC_LEVEL, не может получить прерывания APC. Но оба irQL подразумевают контекст потока и оба подразумевают, что код может быть выгружаем.

Драйверы самого низкого уровня обрабатывают IRP при выполнении в одном из трех списков IRQL:

  • PASSIVE_LEVEL без прерываний, маскированных на процессоре, в подпрограммах диспетчеризации драйвера

    Процедуры DriverEntry, AddDevice, Reinitialize и Unload также выполняются в PASSIVE_LEVEL, как и любые системные потоки, созданные драйвером.

  • DISPATCH_LEVEL с DISPATCH_LEVEL и APC_LEVEL прерываний, маскированных на процессоре, в подпрограмме StartIo

    На DISPATCH_LEVEL также выполняются подпрограммы AdapterControl,AdapterListControl, IoTimer, Cancel (пока она содержит блокировку отмены спина) и CustomTimerDpc, а также подпрограммы DpcForIsr и CustomDpc.

  • Устройство IRQL (DIRQL), со всеми прерываниями, которые меньше или равны SynchronizeIrql объектов прерываний драйвера, маскируются на процессоре, в подпрограммах ISR и SynchCritSection

Большинство драйверов более высокого уровня обрабатывают IRP при выполнении в любом из двух списков IRQL:

  • PASSIVE_LEVEL без прерываний, маскированных на процессоре, в подпрограммах диспетчеризации драйвера

    Процедуры DriverEntry, Reinitialize, AddDevice и Unload также выполняются в PASSIVE_LEVEL, как и любые созданные драйвером системные потоки или подпрограммы обратного вызова рабочих потоков или драйверы файловой системы.

  • DISPATCH_LEVEL с DISPATCH_LEVEL и APC_LEVEL прерываний, маскированных на процессоре, в подпрограммах IoCompletion драйвера

    Процедуры IoTimer, Cancel и CustomTimerDpc также выполняются в DISPATCH_LEVEL.

В некоторых случаях на APC_LEVEL IRQL вызываются драйверы среднего и самого низкого уровня для запоминающих устройств. В частности, это может произойти при сбое страницы, для которой драйвер файловой системы отправляет запрос IRP_MJ_READ на более низкие драйверы.

Большинство стандартных подпрограмм драйвера выполняются в IRQL, что позволяет им просто вызывать соответствующие подпрограммы поддержки. Например, драйвер устройства должен вызывать AllocateAdapterChannel при выполнении в DISPATCH_LEVEL IRQL. Так как большинство драйверов устройств вызывают эти подпрограммы из подпрограммы StartIo , обычно они выполняются в DISPATCH_LEVEL уже.

Обратите внимание, что драйвер устройства, который не имеет подпрограммы StartIo , так как он настраивает и управляет собственными очередями IRP, не обязательно работает на DISPATCH_LEVEL IRQL при вызове AllocateAdapterChannel. Такой драйвер должен вложить свой вызов к AllocateAdapterChannel между вызовами KeRaiseIrql и KeLowerIrql , чтобы он запускал требуемый IRQL при вызове AllocateAdapterChannel и восстанавливал исходный IRQL, когда вызывающая подпрограмма восстанавливает управление.

При вызове подпрограмм поддержки драйверов имейте в виду следующее.

  • Вызов KeRaiseIrql с входным значением NewIrql , которое меньше текущего IRQL, приводит к неустранимой ошибке. Вызов KeLowerIrql , за исключением восстановления исходного IRQL (т. е. после вызова KeRaiseIrql), также приводит к неустранимой ошибке.

  • При выполнении в IRQL >= DISPATCH_LEVEL вызов KeWaitForSingleObject или KeWaitForMultipleObjects для объектов диспетчера, определенных ядром, для ожидания ненулевого интервала приводит к неустранимой ошибке.

  • Единственные подпрограммы драйвера, которые могут безопасно ждать, пока события, семафоры, мьютексы или таймеры будут заданы в состояние сигнала, — это подпрограммы, которые выполняются в контексте неарбитарных потоков на PASSIVE_LEVEL IRQL, таких как потоки, созданные драйвером, подпрограммы DriverEntry и Повторная инициализация или подпрограммы диспетчеризации для синхронных операций ввода-вывода, присущих большинству устройств.

  • Даже при выполнении в PASSIVE_LEVEL IRQL код драйвера для страничных страниц не должен вызывать KeSetEvent, KeReleaseSemaphore или KeReleaseMutex с входным параметром Wait , равным TRUE. Такой вызов может привести к неустранимой ошибке страницы.

  • Любая подпрограмма, выполняющаяся в APC_LEVEL больше IRQL, не может ни выделить память из страничного пула, ни безопасно получить доступ к памяти в выстраивном пуле. Если подпрограмма, выполняемая в IRQL больше APC_LEVEL, вызывает ошибку страницы, это неустранимая ошибка.

  • Драйвер должен работать на DISPATCH_LEVEL IRQL при вызове KeAcquireSpinLockAtDpcLevel и KeReleaseSpinLockFromDpcLevel.

    Драйвер может работать в irQL <= DISPATCH_LEVEL при вызове KeAcquireSpinLock , но он должен освободить этот спин-блокировку, вызвав KeReleaseSpinLock. Другими словами, ошибка программирования заключается в освобождении блокировки спина, полученной с помощью KeAcquireSpinLock путем вызова KeReleaseSpinLockFromDpcLevel.

    Драйвер не должен вызывать KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock или KeReleaseSpinLock при выполнении на DISPATCH_LEVEL IRQL > .

  • Вызов подпрограммы поддержки, использующей спиновую блокировку, например процедуры ExInterlockedXxx , вызывает вызов IRQL на текущем процессоре либо для DISPATCH_LEVEL, либо в DIRQL, если вызывающий объект еще не запущен в вызываемом IRQL.

  • Код драйвера, который выполняется на PASSIVE_LEVEL IRQL > , должен выполняться как можно быстрее. Чем выше irQL, на котором выполняется подпрограмма, тем важнее для хорошей общей производительности настроить ее для выполнения как можно быстрее. Например, любой драйвер, вызывающий KeRaiseIrql , должен выполнить обратный вызов KeLowerIrql как можно скорее.

Дополнительные сведения об определении приоритетов см. в техническом документе Планирование, контекст потока и IRQL .