colas de IRP de Cancel-Safe
Los controladores que implementan su propia cola irP deben usar el marco de cola irP seguro de cancelación . Las colas irP seguras de cancelación dividen el control de IRP en dos partes:
El controlador proporciona un conjunto de rutinas de devolución de llamada que implementan operaciones estándar en la cola irP del controlador. Las operaciones proporcionadas incluyen insertar y quitar IRP de la cola, y bloquear y desbloquear la cola. Consulte Implementación de la cola de IRP de Cancel-Safe.
Siempre que el controlador necesite insertar o quitar realmente un IRP de la cola, usa las rutinas IoCsqXxx proporcionadas por el sistema. Estas rutinas controlan toda la lógica de sincronización e cancelación de IRP para el controlador.
Los controladores que usan colas irP seguras para cancelar no implementan rutinas de cancelación para admitir la cancelación de IRP.
El marco garantiza que los controladores inserten y quiten los IRP de su cola de forma atómica. También garantiza que la cancelación de IRP se implemente correctamente. Los controladores que no usan el marco de trabajo deben bloquear y desbloquear manualmente la cola antes de realizar inserciones y eliminaciones. También deben evitar las condiciones de carrera que pueden dar lugar al implementar una rutina Cancel . (Para obtener una descripción de las condiciones de carrera que pueden surgir, consulte Sincronización de la cancelación de IRP).
El marco de cola irP seguro de cancelación se incluye con Windows XP y versiones posteriores de Windows. Los controladores que también deben funcionar con Windows 2000 y Windows 98/Me pueden vincular a la biblioteca Csq.lib que se incluye en el Kit de controladores de Windows (WDK). La biblioteca Csq.lib proporciona una implementación de este marco.
Las rutinas IoCsqXxx se declaran en Windows XP y versiones posteriores de Wdm.h y Ntddk.h. Los controladores que también deben funcionar con Windows 2000 y Windows 98/Me deben incluir Csq.h para las declaraciones.
Puede ver una demostración completa de cómo usar colas irP seguras para cancelaciones en el directorio \src\general\cancel del WDK. Para obtener más información sobre estas colas, consulte también las notas del producto Flujo de control para Cancel-Safe irP Queuing .
Implementación de la cola de IRP de Cancel-Safe
Para implementar una cola irP segura para cancelación, los controladores deben proporcionar las siguientes rutinas:
Cualquiera de las siguientes rutinas para insertar IRP en la cola: CsqInsertIrp o CsqInsertIrpEx. CsqInsertIrpEx es una versión extendida de CsqInsertIrp; la cola se implementa mediante una u otra.
Rutina CsqRemoveIrp que quita el IRP especificado de la cola.
Rutina CsqPeekNextIrp que devuelve un puntero al siguiente IRP después del IRP especificado en la cola. Aquí es donde el sistema pasa el valor PeekContext que recibe de IoCsqRemoveNextIrp. El controlador puede interpretar ese valor de cualquier manera.
Ambas rutinas siguientes permiten que el sistema bloquee y desbloquee la cola irP: CsqAcquireLock y CsqReleaseLock.
Rutina CsqCompleteCanceledIrp que completa un IRP cancelado.
Los punteros a las rutinas del controlador se almacenan en la estructura IO_CSQ que describe la cola. El controlador asigna el almacenamiento para la estructura de IO_CSQ . Se garantiza que la estructura de IO_CSQ permanezca en un tamaño fijo, por lo que un controlador puede insertar de forma segura la estructura dentro de su extensión del dispositivo.
El controlador usa IoCsqInitialize o IoCsqInitializeEx para inicializar la estructura. Use IoCsqInitialize si la cola implementa CsqInsertIrp o IoCsqInitializeEx si la cola implementa CsqInsertIrpEx.
Los controladores solo necesitan proporcionar la funcionalidad esencial en cada rutina de devolución de llamada. Por ejemplo, solo las rutinas CsqAcquireLock y CsqReleaseLock implementan el control de bloqueos. El sistema llama automáticamente a estas rutinas para bloquear y desbloquear la cola según sea necesario.
Puede implementar cualquier tipo de mecanismo de puesta en cola irP en el controlador, siempre que se proporcionen las rutinas de envío adecuadas. Por ejemplo, el controlador podría implementar la cola como una lista vinculada o como una cola de prioridad.
CsqInsertIrpEx proporciona una interfaz más flexible a la cola que CsqInsertIrp. El controlador puede usar su valor devuelto para indicar el resultado de la operación; si devuelve un código de error, se produjo un error en la inserción. Una rutina CsqInsertIrp no devuelve un valor, por lo que no hay ninguna manera sencilla de indicar que se produjo un error de inserción. Además, CsqInsertIrpEx toma un parámetro InsertContext definido por el controlador adicional que se puede usar para especificar información adicional específica del controlador que va a usar la implementación de la cola.
Los controladores pueden usar CsqInsertIrpEx para implementar un control IRP más sofisticado. Por ejemplo, si no hay IRP pendientes, la rutina CsqInsertIrpEx puede devolver un código de error y el controlador puede procesar el IRP inmediatamente. Del mismo modo, si los IRP ya no se pueden poner en cola, CsqInsertIrpEx puede devolver un código de error para indicar ese hecho.
El controlador está aislado de todo el control de cancelación irP. El sistema proporciona una rutina Cancel para IRP en la cola. Esta rutina llama a CsqRemoveIrp para quitar el IRP de la cola y CsqCompleteCanceledIrp para completar la cancelación de IRP.
En el diagrama siguiente se muestra el flujo de control para la cancelación de IRP.
Una implementación básica de CsqCompleteCanceledIrp es la siguiente.
VOID CsqCompleteCanceledIrp(PIO_CSQ Csq, PIRP Irp) {
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
Los controladores pueden usar cualquiera de los primitivos de sincronización del sistema operativo para implementar sus rutinas CsqAcquireLock y CsqReleaseLock . Los primitivos de sincronización disponibles incluyen bloqueos de número y objetos de exclusión mutua.
Este es un ejemplo de cómo un controlador puede implementar el bloqueo mediante bloqueos de número.
/*
The driver has previously initialized the SpinLock variable with
KeInitializeSpinLock.
*/
VOID CsqAcquireLock(PIO_CSQ IoCsq, PKIRQL PIrql)
{
KeAcquireSpinLock(SpinLock, PIrql);
}
VOID CsqReleaseLock(PIO_CSQ IoCsq, KIRQL Irql)
{
KeReleaseSpinLock(SpinLock, Irql);
}
El sistema pasa un puntero a una variable IRQL a CsqAcquireLock y CsqReleaseLock. Si el controlador usa un bloqueo de número para implementar el bloqueo de la cola, el controlador puede usar esta variable para almacenar el IRQL actual cuando la cola está bloqueada.
Los controladores no son necesarios para usar bloqueos de número. Por ejemplo, el controlador podría usar una exclusión mutua para bloquear la cola. Para obtener una descripción de las técnicas de sincronización que están disponibles para los controladores, consulte Técnicas de sincronización.
Uso de la cola irP de Cancel-Safe
Los controladores usan las siguientes rutinas del sistema al poner en cola y poner en cola irP:
Cualquiera de las siguientes opciones para insertar un IRP en la cola: IoCsqInsertIrp o IoCsqInsertIrpEx.
IoCsqRemoveNextIrp para quitar el siguiente IRP de la cola. Opcionalmente, el controlador puede especificar un valor de clave.
En el diagrama siguiente se muestra el flujo de control para IoCsqRemoveNextIrp.
- IoCsqRemoveIrp para quitar el IRP especificado de la cola.
En el diagrama siguiente se muestra el flujo de control para IoCsqRemoveIrp.
Estas rutinas, a su vez, se envían a rutinas proporcionadas por el controlador.
La rutina IoCsqInsertIrpEx proporciona acceso a las características extendidas de una rutina CsqInsertIrpEx . Devuelve el valor de estado devuelto por CsqInsertIrpEx. El autor de la llamada puede usar este valor para determinar si el IRP se puso en cola correctamente o no. IoCsqInsertIrpEx también permite al autor de la llamada especificar un valor para el parámetro InsertContext de CsqInsertIrpEx.
Tenga en cuenta que tanto IoCsqInsertIrp como IoCsqInsertIrpEx se pueden llamar en cualquier cola segura para cancelación, tanto si la cola tiene una rutina CsqInsertIrp como una rutina CsqInsertIrpEx . IoCsqInsertIrp se comporta igual en cualquier caso. Si IoCsqInsertIrpEx se pasa una cola que tiene una rutina CsqInsertIrp , se comporta de forma idéntica a IoCsqInsertIrp.
En el diagrama siguiente se muestra el flujo de control para IoCsqInsertIrp.
En el diagrama siguiente se muestra el flujo de control para IoCsqInsertIrpEx.
Hay varias maneras naturales de usar las rutinas IoCsqXxx para poner en cola y desquear IRP. Por ejemplo, un controlador simplemente podría poner en cola los IRP que se van a procesar en el orden en que se reciben. El controlador podría poner en cola un IRP de la siguiente manera:
status = IoCsqInsertIrpEx(IoCsq, Irp, NULL, NULL);
Si el controlador no es necesario distinguir entre determinados IRP, podría simplemente poner en cola el orden en el que se ponen en cola, como se indica a continuación:
IoCsqRemoveNextIrp(IoCsq, NULL);
Como alternativa, el controlador podría poner en cola y poner en cola irP específicos. Las rutinas usan la estructura de IO_CSQ_IRP_CONTEXT opaca para identificar irP concretos en la cola. El controlador pone en cola el IRP de la siguiente manera:
IO_CSQ_IRP_CONTEXT ParticularIrpInQueue;
IoCsqInsertIrp(IoCsq, Irp, &ParticularIrpInQueue);
A continuación, el controlador puede quitar de la cola el mismo IRP mediante el valor de IO_CSQ_IRP_CONTEXT .
IoCsqRemoveIrp(IoCsq, Irp, &ParticularIrpInQueue);
Es posible que también sea necesario que el controlador quite los IRP de la cola en función de un criterio determinado. Por ejemplo, el controlador podría asociar una prioridad a cada IRP, de modo que los IRP de mayor prioridad se desqueen primero. El controlador puede pasar un valor PeekContext a IoCsqRemoveNextIrp, que el sistema pasa al controlador cuando solicita el siguiente IRP en la cola.