Compartir a través de


Uso de la sincronización automática

Casi todo el código de un controlador basado en marcos se encuentra en funciones de devolución de llamada de eventos. El marco sincroniza automáticamente la mayoría de las funciones de devolución de llamada de un controlador, como se indica a continuación:

  • El marco siempre sincroniza las funciones de devolución de llamada de eventos de objeto de dispositivo general, de objeto de dispositivo funcional (FDO) y de objeto de dispositivo físico (PDO) entre sí. Solo se puede llamar a una de las funciones de devolución de llamada a la vez para cada dispositivo. La excepción es EvtDeviceSurpriseRemoval, EvtDeviceQueryRemove y EvtDeviceQueryStop. Estas funciones de devolución de llamada admiten los eventos de Plug and Play (PnP) y administración de energía, y se invocan con IRQL = PASSIVE_LEVEL.

  • Opcionalmente, el marco de trabajo puede sincronizar la ejecución de las funciones de devolución de llamada que controlan las solicitudes de E/S de un controlador, de modo que estas funciones de devolución de llamada se ejecuten de una en una. En concreto, el marco puede sincronizar las funciones callback de cola, interrupción, llamada a procedimiento diferido (DPC), temporizador, elemento de trabajo y objetos archivo, junto con la función callback EvtRequestCancel del objeto de solicitud. El marco llama a la mayoría de estas funciones de devolución de llamada con IRQL igual a DISPATCH_LEVEL, pero puede obligar a que las funciones de devolución de llamada de las colas y los objetos de archivo se ejecuten con IRQL igual a PASSIVE_LEVEL. (Las funciones de devolución de llamada de elementos de tarea siempre se ejecutan en PASSIVE_LEVEL).

El marco implementa esta sincronización automática mediante un conjunto de bloqueos de sincronización internos. El framework garantiza que dos o más hilos no puedan llamar al mismo callback al mismo tiempo. Cada hilo debe esperar a poder adquirir un bloqueo de sincronización antes de llamar a un callback. (Opcionalmente, los controladores también pueden adquirir estos bloqueos de sincronización cuando sea necesario. Para obtener más información, consulte Uso de bloqueos de marco).

El controlador debe almacenar datos específicos del objeto en el espacio de contexto del objeto. Si el controlador solo usa interfaces definidas por el marco, solo las funciones de devolución de llamada que reciben un identificador para el objeto pueden acceder a estos datos. Si el entorno está sincronizando las llamadas a las funciones de devolución de llamada del controlador de dispositivo, solo se llama a una función de devolución de llamada a la vez. El espacio de contexto del objeto solo es accesible para una función de devolución de llamada a la vez.

A menos que el controlador implemente el control de interrupciones de nivel pasivo, el código que maneja las interrupciones y accede a los datos de interrupción debe ejecutarse en el IRQL (DIRQL) del dispositivo y requiere una sincronización adicional. Para obtener más información, vea Sincronizar código de interrupción.

Si el controlador habilita la sincronización automática de las funciones de devolución de llamada que controlan las solicitudes de E/S, el marco sincroniza estas funciones de devolución de llamada para que se ejecuten de una en una. En la tabla siguiente se enumeran las funciones de devolución de llamada que sincroniza el framework.

Tipo de objeto Funciones de devolución de llamada sincronizadas

Queue (objeto)

Controladores de solicitudes, EvtIoQueueState, EvtIoResume, EvtIoStop

File (objeto)

Todas las funciones de devolución de llamada

Request (objeto)

EvtRequestCancel

Opcionalmente, el marco también puede sincronizar estas funciones de devolución de llamada con cualquier interrupción, DPC, elemento de trabajo y funciones de devolución de llamada del objeto de temporizador que el controlador proporciona para el dispositivo (excepto la función de devolución de llamada EvtInterruptIsr del objeto de interrupción). Para habilitar esta sincronización adicional, el controlador debe establecer el miembro AutomaticSerialization de estas estructuras de configuración de estos objetos en TRUE.

En resumen, la funcionalidad de sincronización automática del marco proporciona las siguientes características:

  • El marco siempre sincroniza las funciones de devolución de llamada PnP y de administración de energía de cada dispositivo.

  • Opcionalmente, el marco puede sincronizar los controladores de solicitudes de una cola de E/S y algunas funciones de devolución de llamada adicionales (consulte la tabla anterior).

  • Un controlador puede pedir al framework que sincronice las funciones de devolución de llamada para los objetos de interrupción, DPC, elemento de trabajo y temporizador.

  • Los controladores deben sincronizar el código que gestiona interrupciones y acceder a los datos de interrupciones mediante las técnicas que se describen en Sincronización del código de interrupción.

  • El marco de trabajo no sincroniza las demás funciones de devolución de llamada de un controlador, como la función de devolución de llamada CompletionRoutine del controlador o las funciones de devolución de llamada que define el objeto de destino de E/S. En su lugar, el entorno proporciona bloqueos adicionales que los controladores pueden usar para sincronizar estas funciones de devolución de llamada.

Elección de un ámbito de sincronización

Puede elegir que el marco sincronice todas las funciones de devolución de llamada asociadas a todas las colas de E/S de un dispositivo. Como alternativa, puede elegir que el marco sincronice por separado las funciones de devolución de llamada para cada una de las colas de E/S de un dispositivo. Las opciones de sincronización disponibles para el controlador son las siguientes:

  • Sincronización de nivel de dispositivo

    El framework sincroniza las funciones de callback para todas las colas de E/S del dispositivo, ejecutándolas una a una. El framework logra esta sincronización mediante la adquisición del bloqueo de sincronización del dispositivo antes de llamar a una función callback.

  • Sincronización de nivel de cola

    El marco sincroniza las funciones de devolución de llamada para cada cola de E/S individual, de modo que se ejecuten una a la vez. El framework logra esta sincronización mediante la adquisición del bloqueo de sincronización de la cola antes de llamar a una función de devolución de llamada.

  • Sin sincronización

    El framework no sincroniza la ejecución de las funciones de devolución de llamada en la tabla anterior y no establece un cerrojo de sincronización antes de llamar a las funciones de devolución de llamada. Si se requiere sincronización, el controlador debe proporcionarla.

Para especificar si desea que el marco proporcione sincronización de nivel de dispositivo, sincronización de nivel de cola o ninguna sincronización para el controlador, especifique un ámbito de sincronización para el objeto de controlador, los objetos de dispositivo o los objetos de cola. El miembro SynchronizationScope de la estructura WDF_OBJECT_ATTRIBUTES de un objeto identifica el ámbito de sincronización del objeto. Los valores de ámbito de sincronización que puede especificar el controlador son:

WdfSynchronizationScopeDevice
El marco se sincroniza obteniendo el bloqueo de sincronización de un objeto de dispositivo.

WdfSynchronizationScopeQueue
El marco se sincroniza obteniendo el bloqueo de sincronización de un objeto de cola.

WdfSynchronizationScopeNone
El marco no se sincroniza y no obtiene un bloqueo de sincronización.

WdfSynchronizationScopeInheritFromParent
El marco obtiene el valor SynchronizationScope del objeto a partir de su objeto primario.

En general, evite usar la sincronización de nivel de dispositivo.

Para obtener más información sobre los valores de ámbito de sincronización, consulte WDF_SYNCHRONIZATION_SCOPE.

El ámbito de sincronización predeterminado para los objetos de controlador es WdfSynchronizationScopeNone. El ámbito de sincronización predeterminado para los objetos de dispositivo y cola es WdfSynchronizationScopeInheritFromParent.

Para usar la estructura para proporcionar sincronización a nivel de dispositivo para todos los dispositivos, establezca SynchronizationScope en WdfSynchronizationScopeDevice en el atributo de la estructura WDF_OBJECT_ATTRIBUTES del objeto del controlador driver. Utilice el valor predeterminado WdfSynchronizationScopeInheritFromParent para cada objeto de dispositivo.

Para proporcionar sincronización de nivel de dispositivo para dispositivos individuales, use el valor predeterminado WdfSynchronizationScopeNone para el objeto de controlador . Establezca SynchronizationScope en WdfSynchronizationScopeDevice en la estructura WDF_OBJECT_ATTRIBUTES de objetos de dispositivo individuales.

Si desea que el marco proporcione sincronización de nivel de cola para un dispositivo, puede usar las técnicas siguientes:

  • Para las versiones 1.9 y posteriores del marco, habilite la sincronización de nivel de cola para colas individuales estableciendo WdfSynchronizationScopeQueue en la estructura WDF_OBJECT_ATTRIBUTES del objeto queue. Se prefiere esta técnica.

  • Como alternativa, puede usar los pasos siguientes en todas las versiones del marco:

    1. Establezca SynchronizationScope en WdfSynchronizationScopeQueue en la estructura WDF_OBJECT_ATTRIBUTES del objeto de dispositivo .
    2. Utilice el valor predeterminado WdfSynchronizationScopeInheritFromParent para los objetos de cola de cada dispositivo.

Si no desea que el marco sincronice las funciones de devolución de llamada que controlan las solicitudes de E/S del controlador, use el valor predeterminado SynchronizationScope para los objetos driver, device y queue del controlador. En este caso, el entorno no sincroniza automáticamente las funciones de callback relacionadas con la petición de entrada/salida del driver. El entorno puede llamar a los callbacks en IRQL <= DISPATCH_LEVEL.

Al establecer un valor SynchronizationScope , solo se sincronizan las funciones de devolución de llamada que contiene la tabla anterior. Si desea que el marco también sincronice las funciones de devolución de llamada del controlador, DPC, elemento de trabajo y temporizador, establezca el miembro AutomaticSerialization de las estructuras de configuración de estos objetos en TRUE.

Sin embargo, puede establecer AutomaticSerialization en TRUE solo si todas las funciones de devolución de llamada que desea sincronizar se ejecutan en el mismo IRQL. Elegir un nivel de ejecución, que se describe a continuación, podría dar lugar a niveles IRQL incompatibles. En tal situación, el controlador debe usar bloqueos de marco en lugar de establecer AutomaticSerialization. Para obtener más información sobre las estructuras de configuración para los objetos de interrupción, DPC, elemento de trabajo y temporizador, y para obtener más información sobre las restricciones que se aplican a la configuración de AutomaticSerialization en estas estructuras, vea WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIG y WDF_TIMER_CONFIG.

Si establece AutomaticSerialization en TRUE, seleccione sincronización a nivel de cola.

Elección de un nivel de ejecución

Cuando un controlador crea algunos tipos de objetos de marco, puede especificar un nivel de ejecución para el objeto . El nivel de ejecución especifica el IRQL en el que el framework llama a las funciones callback de eventos del objeto que manejan las solicitudes de E/S de un controlador.

Si un controlador suministra un nivel de ejecución, el nivel suministrado afecta a las funciones de devolución de llamada para los objetos de cola y de archivo. Normalmente, si el controlador usa la sincronización automática, el framework llama a estas funciones de devolución de llamada con IRQL = DISPATCH_LEVEL. Al especificar un nivel de ejecución, el controlador puede forzar que el entorno llame a estas funciones de devolución de llamada con IRQL = PASSIVE_LEVEL. El framework utiliza las siguientes reglas al establecer el IRQL para llamar a las funciones de callback de cola y de objeto de archivo:

  • Si un controlador usa la sincronización automática, el marco llama a sus funciones de devolución de llamada de cola y objeto de archivo en IRQL = DISPATCH_LEVEL a menos que el controlador pida al marco que llame a sus funciones de devolución de llamada en IRQL = PASSIVE_LEVEL.

  • Si un controlador no usa la sincronización automática y no especifica un nivel de ejecución, el marco puede llamar a las funciones de devolución de llamada de cola y objeto de archivo del controlador en IRQL <= DISPATCH_LEVEL.

Si el controlador proporciona funciones de devolución de llamada de objeto de archivo, probablemente desee que el entorno llame a estas funciones de devolución de llamada con IRQL = PASSIVE_LEVEL porque algunos datos de archivo, como el nombre del archivo, son paginables.

Para proporcionar un nivel de ejecución, el controlador debe especificar un valor para el miembro ExecutionLevel de la estructura WDF_OBJECT_ATTRIBUTES de un objeto. Los valores de nivel de ejecución que puede especificar el controlador son:

WdfExecutionLevelPassive
El framework invoca las funciones de devolución de llamada del objeto con IRQL = PASSIVE_LEVEL.

WdfExecutionLevelDispatch
El entorno puede invocar las funciones de devolución de llamada del objeto a nivel de IRQL <= DISPATCH_LEVEL. Si el controlador usa la sincronización automática, el marco siempre llama a las funciones de devolución de llamada en IRQL = DISPATCH_LEVEL.

WdfExecutionLevelInheritFromParent
El framework obtiene el valor ExecutionLevel del padre del objeto.

El nivel de ejecución predeterminado para los objetos de controlador es WdfExecutionLevelDispatch. El nivel de ejecución predeterminado para todos los demás objetos es WdfExecutionLevelInheritFromParent.

Para obtener más información sobre los valores de nivel de ejecución, consulte WDF_EXECUTION_LEVEL.

En la siguiente tabla se muestra el nivel de IRQL al cual el framework es capaz de llamar a las funciones de devolución de llamada de un controlador para objetos de cola y objetos de archivo.

Ámbito de sincronización Nivel de ejecución IRQL de las funciones de devolución de llamada de cola y archivo

WdfSynchronizationScopeDevice

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelDispatch

<= NIVEL_DESPACHO

Puede establecer el nivel de ejecución en WdfExecutionLevelPassive o WdfExecutionLevelDispatch para los objetos driver, device, file, queue, timer y general. Para otros objetos, solo puede establecer WdfExecutionLevelInheritFromParent.

Especifique WdfExecutionLevelPassive si:

  • Las funciones de devolución de llamada del controlador deben llamar a métodos de marco o rutinas del modelo de controladores de Windows (WDM) que solo se pueden llamar en IRQL = PASSIVE_LEVEL.

  • Las funciones de devolución de llamada del controlador deben tener acceso a código o datos paginables. Por ejemplo, las funciones callback de objetos de archivo suelen acceder a datos paginables.

En lugar de establecer WdfExecutionLevelPassive, el controlador puede establecer WdfExecutionLevelDispatch y proporcionar una función de callback que cree elementos de trabajo si debe manejar algunas operaciones en IRQL = PASSIVE_LEVEL.

Antes de decidir si el controlador debe establecer el nivel de ejecución de un objeto en WdfExecutionLevelPassive, determine el IRQL en el que se llama al controlador y a otros controladores de la pila de controladores. Tenga en cuenta las siguientes situaciones:

  • Si el controlador está en la parte superior de la pila de controladores de modo kernel, el sistema normalmente invoca al controlador cuando IRQL = PASSIVE_LEVEL. El cliente de este tipo de controlador puede ser un controlador basado en UMDF o una aplicación en modo de usuario. Especificar WdfExecutionLevelPassive no afecta negativamente al rendimiento del controlador, ya que el entorno no tiene que poner en cola las llamadas de su controlador a elementos de trabajo que se invocan en IRQL = PASSIVE_LEVEL.

  • Si su controlador no está en la parte superior del stack, es probable que el sistema no llame a su controlador con IRQL = PASSIVE_LEVEL. Por lo tanto, el framework debe encolar las llamadas del controlador a los elementos de trabajo, que se invocan posteriormente en IRQL = PASSIVE_LEVEL. Este proceso puede provocar un rendimiento deficiente del controlador, en comparación con la posibilidad de llamar a las funciones de devolución de llamada del controlador en IRQL <= DISPATCH_LEVEL.

Para los objetos DPC y para los objetos de temporizador que no representan temporizadores de nivel pasivo, no puede establecer el miembro AutomaticSerialization de la estructura de configuración en TRUE si establece el nivel de ejecución del dispositivo primario en WdfExecutionLevelPassive. El marco adquiere los bloqueos de sincronización de devolución de llamada del objeto de dispositivo en IRQL = PASSIVE_LEVEL. Por lo tanto, no puede usar los bloqueos para sincronizar las funciones de devolución de llamada de objetos DPC o timer, que deben ejecutarse en IRQL = DISPATCH_LEVEL. En este caso, el controlador debería usar framework spin locks en cualquier dispositivo, DPC o función de devolución de llamada de un objeto de temporizador que deba sincronizarse con las demás.

Tenga en cuenta también que para los objetos de temporizador que representan temporizadores de nivel pasivo, puede establecer el miembro AutomaticSerialization de la estructura de configuración en TRUE solo si el nivel de ejecución del dispositivo primario está establecido en WdfExecutionLevelPassive.