Uso de la sincronización automática
Casi todo el código de un controlador basado en marcos reside 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 entre sí funciones generales de devolución de llamada de dispositivo, objeto de dispositivo funcional (FDO) y objeto de dispositivo físico (PDO) entre sí para que solo se pueda llamar a una de las funciones de devolución de llamada (excepto EvtDeviceSurpriseRemoval, EvtDeviceQueryRemove y EvtDeviceQueryStop) a la vez para cada dispositivo. Estas funciones de devolución de llamada admiten eventos de administración de energía y Plug and Play (PnP) y se llaman en IRQL = PASSIVE_LEVEL.
Opcionalmente, el marco 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 de devolución de llamada para los objetos queue, interrupt, deferred procedure call (DPC),timer, work-item y file , junto con la función de devolución de llamada EvtRequestCancel del objeto de solicitud. El marco llama a la mayoría de estas funciones de devolución de llamada en IRQL = DISPATCH_LEVEL, pero puede forzar que las funciones de devolución de llamada de cola y objeto de archivo se ejecuten en IRQL = PASSIVE_LEVEL. (Las funciones de devolución de llamada de elemento de trabajo siempre se ejecutan en PASSIVE_LEVEL).
El marco implementa esta sincronización automática mediante un conjunto de bloqueos de sincronización internos. El marco garantiza que dos o más subprocesos no puedan llamar a la misma función de devolución de llamada al mismo tiempo, ya que cada subproceso debe esperar hasta que pueda adquirir un bloqueo de sincronización antes de llamar a una función de devolución de llamada. (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 marco está sincronizando llamadas a las funciones de devolución de llamada del controlador, solo se llamará a una función de devolución de llamada cada vez y el espacio de contexto del objeto será accesible solo 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 atiende interrupciones y accede a los datos de interrupción debe ejecutarse en el IRQL (DIRQL) del dispositivo y requiere 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 marco de trabajo.
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 |
Objeto de solicitud |
Opcionalmente, el marco también puede sincronizar estas funciones de devolución de llamada con cualquier función de devolución de llamada de objeto de interrupción, DPC, elemento de trabajo y 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 marco 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 los servicios interrumpen y acceden a los datos de interrupción 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 marco 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 marco sincroniza las funciones de devolución de llamada que contiene la tabla anterior, para todas las colas de E/S del dispositivo, de modo que se ejecuten de una en una. El marco logra esta sincronización mediante la adquisición del bloqueo de sincronización del dispositivo antes de llamar a una función de devolución de llamada.
Sincronización de nivel de cola
El marco sincroniza las funciones de devolución de llamada que contiene la tabla anterior, para cada cola de E/S individual, de modo que se ejecuten de una en una. El marco 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 marco no sincroniza la ejecución de las funciones de devolución de llamada que contiene la tabla anterior y no adquiere un bloqueo 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, puede especificar 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 el controlador puede especificar son:
WdfSynchronizationScopeDevice
El marco se sincroniza mediante la obtención del 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 del objeto primario.
En general, no se recomienda 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.
Si desea que el marco proporcione sincronización de nivel de dispositivo para todos los dispositivos, puede seguir estos pasos:
Establezca SynchronizationScope en WdfSynchronizationScopeDevice en la estructura WDF_OBJECT_ATTRIBUTES del objeto driver del controlador .
Use el valor predeterminado WdfSynchronizationScopeInheritFromParent para cada objeto de dispositivo .
Como alternativa, para proporcionar sincronización de nivel de dispositivo para dispositivos individuales, puede seguir estos pasos:
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, están disponibles las técnicas siguientes:
Para las versiones 1.9 y posteriores del marco, debe habilitar la sincronización de nivel de cola para colas individuales estableciendo WdfSynchronizationScopeQueue en la estructura WDF_OBJECT_ATTRIBUTES del objeto queue. Esta es la técnica preferida.
Como alternativa, puede usar los pasos siguientes en todas las versiones del marco:
- Establezca SynchronizationScope en WdfSynchronizationScopeQueue en la estructura WDF_OBJECT_ATTRIBUTES del objeto de dispositivo .
- Use 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 synchronizationScope predeterminado para los objetos driver, device y queue del controlador. En este caso, el marco no sincroniza automáticamente las funciones de devolución de llamada relacionadas con la solicitud de E/S del controlador y se puede llamar a las funciones de devolución de llamada en IRQL <= DISPATCH_LEVEL.
Tenga en cuenta que 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 de objeto de interrupción del controlador, DPC, elemento de trabajo y temporizador, el controlador debe establecer 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, debe seleccionar sincronización de 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 marco llamará a las funciones de devolución de llamada de eventos del objeto que controlan las solicitudes de E/S de un controlador.
Si un controlador proporciona un nivel de ejecución, el nivel proporcionado afecta a las funciones de devolución de llamada para los objetos de cola y archivo. Normalmente, si el controlador usa la sincronización automática, el marco llama a estas funciones de devolución de llamada en IRQL = DISPATCH_LEVEL. Al especificar un nivel de ejecución, el controlador puede forzar que el marco llame a estas funciones de devolución de llamada en IRQL = PASSIVE_LEVEL. El marco de trabajo usa las siguientes reglas al establecer irQL en la que se llama a las funciones de devolución de llamada de objeto de cola y archivo:
Si un controlador usa la sincronización automática, se 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 controlador 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, se puede llamar a las funciones de devolución de llamada de cola y objeto de archivo del controlador en IRQL <= DISPATCH_LEVEL.
Tenga en cuenta que si el controlador proporciona funciones de devolución de llamada de objetos de archivo, lo más probable es que quiera que el marco llame a estas funciones de devolución de llamada en IRQL = PASSIVE_LEVEL porque algunos datos de archivo, como el nombre de 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 marco llama a las funciones de devolución de llamada del objeto en IRQL = PASSIVE_LEVEL.
WdfExecutionLevelDispatch
El marco de trabajo puede llamar a las funciones de devolución de llamada del objeto en 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 marco obtiene el valor ExecutionLevel del objeto del elemento primario 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 tabla siguiente se muestra el nivel IRQL en el que el marco puede 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 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 |
<= DISPATCH_LEVEL |
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 se permite WdfExecutionLevelInheritFromParent .
Debe especificar WdfExecutionLevelPassive si:
Las funciones de devolución de llamada del controlador deben llamar a métodos de marco o rutinas del modelo de controlador 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 datos o código paginable. (Por ejemplo, las funciones de devolución de llamada de objetos de archivo suelen tener acceso a datos paginables).
En lugar de establecer WdfExecutionLevelPassive, el controlador puede establecer WdfExecutionLevelDispatch y proporcionar una función de devolución de llamada que cree elementos de trabajo si debe controlar algunas operaciones en IRQL = PASSIVE_LEVEL.
Antes de decidir si el controlador debe establecer el nivel de ejecución de un objeto en WdfExecutionLevelPassive, debe determinar 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 en modo kernel, el sistema normalmente llama al controlador en 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. La especificación de WdfExecutionLevelPassive no afecta negativamente al rendimiento del controlador, ya que el marco no tiene que poner en cola las llamadas del controlador a elementos de trabajo que se llaman en IRQL = PASSIVE_LEVEL.
Si el controlador no está en la parte superior de la pila, es probable que el sistema no llame al controlador en IRQL = PASSIVE_LEVEL. Por lo tanto, el marco debe poner en cola las llamadas del controlador a los elementos de trabajo, a los que posteriormente se llama en IRQL = PASSIVE_LEVEL. Este proceso puede provocar un rendimiento deficiente del controlador, en comparación con lo que permite 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, tenga en cuenta que no puede establecer el miembro AutomaticSerialization de la estructura de configuración en TRUE si ha establecido el nivel de ejecución del dispositivo primario en WdfExecutionLevelPassive. Esto se debe a que el marco adquirirá los bloqueos de sincronización de devolución de llamada del objeto de dispositivo en IRQL = PASSIVE_LEVEL y, por lo tanto, los bloqueos no se pueden usar para sincronizar las funciones de devolución de llamada de objetos DPC o temporizador, que deben ejecutarse en IRQL = DISPATCH_LEVEL. En tal caso, el controlador debe usar bloqueos de número de marco en cualquier dispositivo, DPC o funciones de devolución de llamada de objeto de temporizador que se deben sincronizar entre 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.