Поделиться через


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

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

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

Пассивный уровень
Прерывания отключены — отсутствуют.

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

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

Подпрограммы драйверов, вызываемые в APC_LEVEL — некоторые подпрограммы отправки (см. статью "Подпрограммы отправки" и irQLs).

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

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

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

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

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

Драйверы низкого уровня обрабатывают IRP, работая на одном из трех уровней IRQL.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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