Uso de objetos de temporizador
En la ilustración siguiente se muestra el uso de un temporizador de notificación para configurar un intervalo de tiempo de espera para una operación y, a continuación, esperar mientras otras rutinas del controlador procesan una solicitud de E/S.
Como se muestra en la ilustración anterior, un controlador debe proporcionar almacenamiento para el objeto de temporizador, que debe inicializarse mediante una llamada a KeInitializeTimer con un puntero a este almacenamiento. Normalmente, un controlador realiza esta llamada desde su rutina AddDevice .
Dentro del contexto de un subproceso determinado, como un subproceso creado por el controlador o un subproceso que solicita una operación de E/S sincrónica, el controlador puede esperar a su objeto de temporizador, como se muestra en la ilustración anterior:
El subproceso llama a KeSetTimer con un puntero al objeto de temporizador y un valor DueTime determinado, expresado en unidades de 100 nanosegundos. Un valor positivo para DueTime especifica una hora absoluta en la que el objeto de temporizador debe quitarse de la cola del temporizador del kernel y establecer en el estado Signaled. Un valor negativo para DueTime especifica un intervalo relativo a la hora actual del sistema.
Tenga en cuenta que el subproceso (o la rutina de controlador que se ejecuta en un subproceso del sistema) pasa un puntero NULL para el objeto DPC (mostrado anteriormente en la ilustración que ilustra el uso del temporizador y los objetos DPC para una rutina CustomTimerDpc) cuando llama a KeSetTimer Si espera en el objeto de temporizador en lugar de poner en cola una rutina CustomTimerDpc .
El subproceso llama a KeWaitForSingleObject con un puntero al objeto de temporizador, que coloca el subproceso en un estado de espera mientras el objeto de temporizador está en la cola del temporizador del kernel.
El dueTime especificado expira.
El kernel quita la cola del objeto de temporizador, la establece en estado Signaled y cambia el estado del subproceso de esperar a listo.
El kernel envía el subproceso para su ejecución tan pronto como un procesador está disponible: es decir, ningún otro subproceso con una prioridad más alta está actualmente en estado listo y no hay rutinas en modo kernel que se ejecutarán en un IRQL superior.
Las rutinas de controlador que se ejecutan en IRQL >= DISPATCH_LEVEL pueden agotar el tiempo de espera de las solicitudes mediante un objeto de temporizador con un objeto DPC asociado para poner en cola una rutina CustomTimerDpc proporcionada por el controlador. Solo las rutinas de controlador que se ejecutan dentro de un contexto de subproceso nobitrario pueden esperar un intervalo distinto de cero en un objeto de temporizador, como se muestra en la ilustración anterior.
Al igual que todos los demás subprocesos, un subproceso creado por controladores se representa mediante un objeto de subproceso de kernel, que también es un objeto distribuidor. Por lo tanto, un controlador no necesita que su subproceso creado por el controlador use un objeto de temporizador para colocarse voluntariamente en un estado de espera para un intervalo determinado. En su lugar, el subproceso puede llamar a KeDelayExecutionThread con un intervalo proporcionado por el autor de la llamada. Para obtener más información sobre esta técnica, vea Sondear un dispositivo.
Las rutinas DriverEntry, Reinitialize y Unload también se ejecutan en un contexto de subproceso del sistema, por lo que los controladores pueden llamar a KeWaitForSingleObject con un objeto de temporizador inicializado por el controlador o KeDelayExecutionThread mientras se inicializan o descargan. Un controlador de dispositivo puede llamar a KeStallExecutionProcessor durante un intervalo muy corto (preferiblemente algo inferior a 50 microsegundos) si debe esperar a que el dispositivo actualice el estado durante su inicialización.
Sin embargo, los controladores de nivel superior suelen usar otro mecanismo de sincronización en sus rutinas DriverEntry y Reinicializar en lugar de usar un objeto de temporizador. Los controladores de nivel superior siempre deben diseñarse para superponerse sobre cualquier controlador de nivel inferior de un tipo o tipo de dispositivo determinado. Por lo tanto, un controlador de nivel superior tiende a ser lento para cargarse si espera en un objeto de temporizador o llama a KeDelayExecutionThread porque este controlador debe esperar un intervalo lo suficientemente largo como para dar cabida al dispositivo más lento posible que lo admita. Tenga en cuenta también que un "seguro" pero un intervalo mínimo para dicha espera es muy difícil de determinar.
Del mismo modo, los controladores PnP no deben esperar a que se produzcan otras acciones, sino que deben usar el mecanismo de notificación del administrador de PnP.