Administración de prioridades de hardware
El IRQL en el que se ejecuta una rutina de controlador determina a qué rutinas admite el controlador en modo kernel a las que puede llamar. Por ejemplo, algunas rutinas de compatibilidad con controladores requieren que el autor de la llamada se ejecute en IRQL = DISPATCH_LEVEL. No se puede llamar a otros de forma segura si el autor de la llamada se ejecuta en cualquier IRQL superior a PASSIVE_LEVEL.
A continuación se muestra una lista de irCL en las que se llama a las rutinas de controlador estándar más implementadas. Las IRCL se enumeran de menor a mayor prioridad.
PASSIVE_LEVEL
Interrupciones enmascaradas desactivadas : ninguna.
Rutinas de controlador llamadas en PASSIVE_LEVEL: DriverEntry, AddDevice, Reinitialize, Rutinas de descarga , mayoría de rutinas de envío, subprocesos creados por controladores, devoluciones de llamada de subproceso de trabajo.
APC_LEVEL
Interrupciones enmascaradas desactivadas : APC_LEVEL las interrupciones se enmascaran.
Rutinas de controlador llamadas en APC_LEVEL: algunas rutinas de envío (consulte Rutinas de envío e IRQL).
DISPATCH_LEVEL
Interrupciones enmascaradas: DISPATCH_LEVEL y las interrupciones de APC_LEVEL se enmascaran. Se pueden producir interrupciones de dispositivo, reloj y error de energía.
Rutinas de controlador llamadas en DISPATCH_LEVEL: StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (mientras mantiene presionado el bloqueo de número de cancelación), DpcForIsr, CustomTimerDpc, rutinas CustomDpc .
DIRQL
Interrupciones enmascaradas desactivadas : todas las interrupciones en IRQL <= DIRQL del objeto de interrupción del controlador. Las interrupciones del dispositivo con un valor DIRQL superior pueden producirse, junto con interrupciones de reloj y de error de alimentación.
Rutinas de controlador llamadas en DIRQL: rutinas de InterruptService, SynchCritSection .
La única diferencia entre APC_LEVEL y PASSIVE_LEVEL es que un proceso que se ejecuta en APC_LEVEL no puede obtener interrupciones de APC. Pero ambos IRCL implican un contexto de subproceso y ambos implican que el código se puede paginar.
Los controladores de nivel más bajo procesan irP mientras se ejecutan en una de las tres IRQL:
PASSIVE_LEVEL, sin interrupciones enmascaradas en el procesador, en las rutinas de distribución del controlador
Las rutinas DriverEntry, AddDevice, Reinitialize y Unload también se ejecutan en PASSIVE_LEVEL, como son los subprocesos del sistema creados por el controlador.
DISPATCH_LEVEL, con DISPATCH_LEVEL y APC_LEVEL interrupciones enmascaradas en el procesador, en la rutina StartIo
AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (mientras contiene el bloqueo de número de cancelación) y las rutinas CustomTimerDpc también se ejecutan en DISPATCH_LEVEL, como son las rutinas DpcForIsr y CustomDpc .
IRQL del dispositivo (DIRQL), con todas las interrupciones en menos o igual que synchronizeIrql de los objetos de interrupción del controlador enmascarados en el procesador, en las rutinas ISR y SynchCritSection .
La mayoría de los controladores de nivel superior procesan irP mientras se ejecutan en cualquiera de las dos IRQL:
PASSIVE_LEVEL, sin interrupciones enmascaradas en el procesador, en las rutinas de envío del controlador
Las rutinas DriverEntry, Reinitialize, AddDevice y Unload también se ejecutan en PASSIVE_LEVEL, como son los subprocesos del sistema creados por controladores o rutinas de devolución de llamada de subproceso de trabajo o controladores del sistema de archivos.
DISPATCH_LEVEL, con DISPATCH_LEVEL y APC_LEVEL interrupciones enmascaradas en el procesador, en las rutinas ioCompletion del controlador
Las rutinas IoTimer, Cancel y CustomTimerDpc también se ejecutan en DISPATCH_LEVEL.
En algunas circunstancias, los controladores intermedios y de nivel más bajo de los dispositivos de almacenamiento masivo se llaman en IRQL APC_LEVEL. En concreto, esto puede producirse en un error de página para el que un controlador del sistema de archivos envía una solicitud de IRP_MJ_READ a los controladores inferiores.
La mayoría de las rutinas de controlador estándar se ejecutan en un IRQL que les permite simplemente llamar a las rutinas de soporte técnico adecuadas. Por ejemplo, un controlador de dispositivo debe llamar a AllocateAdapterChannel mientras se ejecuta en irQL DISPATCH_LEVEL. Dado que la mayoría de los controladores de dispositivos llaman a estas rutinas desde una rutina StartIo , normalmente se ejecutan en DISPATCH_LEVEL ya.
Tenga en cuenta que un controlador de dispositivo que no tiene ninguna rutina StartIo porque configura y administra sus propias colas de IRP no se ejecuta necesariamente en DISPATCH_LEVEL IRQL cuando debe llamar a AllocateAdapterChannel. Este controlador debe anidar su llamada a AllocateAdapterChannel entre las llamadas a KeRaiseIrql y KeLowerIrql para que se ejecute en el IRQL necesario cuando llame a AllocateAdapterChannel y restaure el IRQL original cuando la rutina de llamada recupera el control.
Al llamar a rutinas de soporte técnico del controlador, tenga en cuenta lo siguiente.
Al llamar a KeRaiseIrql con un valor NewIrql de entrada menor que el IRQL actual, se produce un error irrecuperable. Llamar a KeLowerIrql excepto para restaurar el IRQL original (es decir, después de una llamada a KeRaiseIrql) también provoca un error irrecuperable.
Mientras se ejecuta en IRQL >= DISPATCH_LEVEL, llamar a KeWaitForSingleObject o KeWaitForMultipleObjects para que los objetos de distribuidor definidos por el kernel esperen a un intervalo distinto de cero provoca un error irrecuperable.
Las únicas rutinas de controlador que pueden esperar de forma segura eventos, semáforos, exclusión mutua o temporizadores que se van a establecer en el estado señalado son aquellos que se ejecutan en un contexto de subproceso nobitrario en irQL PASSIVE_LEVEL, como subprocesos creados por controladores, las rutinas DriverEntry y Reinitialize , o las rutinas de envío para operaciones de E/S sincrónicas inherentemente (como la mayoría de las solicitudes de control de E/S del dispositivo).
Incluso mientras se ejecuta en irQL PASSIVE_LEVEL, el código de controlador paginable no debe llamar a KeSetEvent, KeReleaseSemaphore o KeReleaseMutex con el parámetro Wait de entrada establecido en TRUE. Esta llamada puede provocar un error grave en la página.
Cualquier rutina que se ejecute en mayor que IRQL APC_LEVEL no puede asignar memoria del grupo paginado ni acceder a la memoria en el grupo paginado de forma segura. Si una rutina que se ejecuta en IRQL mayor que APC_LEVEL provoca un error de página, es un error irrecuperable.
Un controlador debe ejecutarse en IRQL DISPATCH_LEVEL cuando llama a KeAcquireSpinLockAtDpcLevel y KeReleaseSpinLockFromDpcLevel.
Un controlador se puede ejecutar en IRQL <= DISPATCH_LEVEL cuando llama a KeAcquireSpinLock , pero debe liberar ese bloqueo de número llamando a KeReleaseSpinLock. Es decir, es un error de programación liberar un bloqueo de número adquirido con KeAcquireSpinLock llamando a KeReleaseSpinLockFromDpcLevel.
Un controlador no debe llamar a KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock o KeReleaseSpinLock mientras se ejecuta en IRQL > DISPATCH_LEVEL.
Al llamar a una rutina de soporte técnico que usa un bloqueo de número, como una rutina ExInterlockedXxx , se genera IRQL en el procesador actual para DISPATCH_LEVEL o para DIRQL si el autor de la llamada aún no se está ejecutando en un IRQL elevado.
El código de controlador que se ejecuta en IRQL > PASSIVE_LEVEL debe ejecutarse lo antes posible. Cuanto mayor sea el IRQL en el que se ejecuta una rutina, más importante es que el buen rendimiento general ajuste esa rutina para que se ejecute lo más rápido posible. Por ejemplo, cualquier controlador que llame a KeRaiseIrql debe realizar la llamada recíproca a KeLowerIrql tan pronto como pueda.
Para obtener más información sobre cómo determinar prioridades, consulte las notas del producto Programación, Contexto de subprocesos e IRQL .