Использование автоматической синхронизации

Почти весь код в драйвере на основе платформы находится в функциях обратного вызова событий. Платформа автоматически синхронизирует большинство функций обратного вызова драйвера следующим образом:

Платформа реализует эту автоматическую синхронизацию с помощью набора внутренних блокировок синхронизации. Платформа гарантирует, что два или более потоков не смогут одновременно вызывать одну и ту же функцию обратного вызова, так как каждый поток должен подождать, пока он не получит блокировку синхронизации, прежде чем вызывать функцию обратного вызова. (При необходимости драйверы также могут получать эти блокировки синхронизации. Дополнительные сведения см. в разделе Использование блокировок платформы.)

Драйвер должен хранить данные, относящиеся к объектам, в пространстве контекста объекта. Если драйвер использует только интерфейсы, определенные платформой, доступ к этим данным могут получить только функции обратного вызова, которые получают дескриптор объекта. Если платформа синхронизирует вызовы с функциями обратного вызова драйвера, за один раз будет вызываться только одна функция обратного вызова, а контекстное пространство объекта будет доступно только для одной функции обратного вызова за раз.

Если драйвер не реализует обработку прерываний пассивного уровня, код, который службы прерывает и обращается к данным прерываний, должен выполняться в IRQL устройства (DIRQL) и требует дополнительной синхронизации. Дополнительные сведения см. в разделе Синхронизация кода прерывания.

Если драйвер включает автоматическую синхронизацию функций обратного вызова, обрабатывающих запросы ввода-вывода, платформа синхронизирует эти функции обратного вызова, чтобы они выполнялись по одной за раз. В следующей таблице перечислены функции обратного вызова, которые платформа синхронизирует.

Тип объекта Синхронизированные функции обратного вызова

Объект Queue

Обработчики запросов, EvtIoQueueState, EvtIoResume, EvtIoStop

File - объект

Все функции обратного вызова

Объект запроса

EvtRequestCancel

При необходимости платформа также может синхронизировать эти функции обратного вызова с любыми функциями обратного вызова прерываний, DPC, рабочих элементов и объектов таймера, которые драйвер предоставляет для устройства (за исключением функции обратного вызова EvtInterruptIsr объекта прерывания). Чтобы включить эту дополнительную синхронизацию, драйвер должен задать для элемента AutomaticSerialization структуры конфигурации этих объектов значение TRUE.

Таким образом, функция автоматической синхронизации платформы предоставляет следующие возможности:

  • Платформа всегда синхронизирует функции обратного вызова PnP и управления питанием каждого устройства.

  • При необходимости платформа может синхронизировать обработчики запросов очереди ввода-вывода и несколько дополнительных функций обратного вызова (см. предыдущую таблицу).

  • Драйвер может попросить платформу синхронизировать функции обратного вызова для объектов прерываний, DPC, рабочих элементов и таймеров.

  • Драйверы должны синхронизировать код, который службы прерывают и обращаются к данным прерываний, используя методы, описанные в разделе Синхронизация кода прерывания.

  • Платформа не синхронизирует другие функции обратного вызова драйвера, такие как функция обратного вызова Драйвера CompletionRoutine или функции обратного вызова, определяемые целевым объектом ввода-вывода. Вместо этого платформа предоставляет дополнительные блокировки , которые драйверы могут использовать для синхронизации этих функций обратного вызова.

Выбор области синхронизации

Вы можете синхронизировать платформу все функции обратного вызова, связанные со всеми очередями ввода-вывода устройства. Кроме того, можно выбрать, чтобы платформа отдельно синхронизирула функции обратного вызова для каждой очереди ввода-вывода устройства. Ниже перечислены параметры синхронизации, доступные для драйвера.

  • Синхронизация на уровне устройства

    Платформа синхронизирует функции обратного вызова, содержащиеся в предыдущей таблице, для всех очередей ввода-вывода устройства, чтобы они запускались по одной за раз. Платформа обеспечивает такую синхронизацию, получив блокировку синхронизации устройства перед вызовом функции обратного вызова.

  • Синхронизация на уровне очереди

    Платформа синхронизирует функции обратного вызова, содержащиеся в предыдущей таблице, для каждой отдельной очереди ввода-вывода, чтобы они выполнялись по одной за раз. Платформа достигает такой синхронизации путем получения блокировки синхронизации очереди перед вызовом функции обратного вызова.

  • Синхронизации нет

    Платформа не синхронизирует выполнение функций обратного вызова, содержащихся в предыдущей таблице, и не получает блокировку синхронизации перед вызовом функций обратного вызова. Если требуется синхронизация, драйвер должен предоставить ее.

Чтобы указать, должна ли платформа обеспечивать синхронизацию на уровне устройства, синхронизацию на уровне очереди или отсутствие синхронизации для драйвера, можно указать область синхронизации для объекта драйвера, объектов устройств или объектов очередей. Элемент SynchronizationScope структуры WDF_OBJECT_ATTRIBUTES объекта определяет область синхронизации объекта. Драйвер может указать следующие значения область синхронизации:

WdfSynchronizationScopeDevice
Платформа синхронизируется, получая блокировку синхронизации объекта устройства.

WdfSynchronizationScopeQueue
Платформа синхронизируется путем получения блокировки синхронизации объекта очереди.

WdfSynchronizationScopeNone
Платформа не синхронизируется и не получает блокировку синхронизации.

WdfSynchronizationScopeInheritFromParent
Платформа получает значение SynchronizationScope объекта из родительского объекта.

Как правило, не рекомендуется использовать синхронизацию на уровне устройства.

Дополнительные сведения о значениях область синхронизации см. в разделе WDF_SYNCHRONIZATION_SCOPE.

По умолчанию для объектов драйвера используется область синхронизации WdfSynchronizationScopeNone. Область синхронизации по умолчанию для объектов устройств и очередей — WdfSynchronizationScopeInheritFromParent.

Если вы хотите, чтобы платформа обеспечивала синхронизацию на уровне устройства для всех устройств, можно выполнить следующие действия.

  1. Для параметра SynchronizationScope задайте значение WdfSynchronizationScopeDevice в WDF_OBJECT_ATTRIBUTES структуре объекта драйвера драйвера.

  2. Используйте значение WdfSynchronizationScopeInheritFromParent по умолчанию для каждого объекта устройства .

Кроме того, чтобы обеспечить синхронизацию на уровне устройства для отдельных устройств, можно выполнить следующие действия.

  1. Используйте значение WdfSynchronizationScopeNone по умолчанию для объекта драйвера .

  2. Задайте для параметра SynchronizationScope значение WdfSynchronizationScopeDevice в WDF_OBJECT_ATTRIBUTES структуре отдельных объектов устройства .

Если вы хотите, чтобы платформа обеспечивала синхронизацию на уровне очередей для устройства, доступны следующие методы:

  • Для платформы версии 1.9 и более поздних следует включить синхронизацию на уровне очередей для отдельных очередей, задав WdfSynchronizationScopeQueue в WDF_OBJECT_ATTRIBUTES структуре объекта очереди. Это предпочтительный метод.

  • Кроме того, можно выполнить следующие действия во всех версиях платформы:

    1. Для параметра SynchronizationScope задайте значение WdfSynchronizationScopeQueue в WDF_OBJECT_ATTRIBUTES структуре объекта устройства .
    2. Используйте значение по умолчанию WdfSynchronizationScopeInheritFromParent для объектов очереди каждого устройства.

Если вы не хотите, чтобы платформа синхронизировала функции обратного вызова, обрабатывающие запросы ввода-вывода драйвера, используйте значение по умолчанию SynchronizationScope для объектов драйвера, устройства и очереди. В этом случае платформа не синхронизирует автоматически функции обратного вызова, связанные с запросами ввода-вывода драйвера, и функции обратного вызова можно вызывать по адресу IRQL <= DISPATCH_LEVEL.

Обратите внимание, что при установке значения SynchronizationScope синхронизируются только функции обратного вызова, содержащиеся в предыдущей таблице. Если требуется, чтобы платформа также синхронизировала функции обратного вызова объекта драйвера, DPC, рабочего элемента и таймера, драйвер должен задать для элемента AutomaticSerialization структуры конфигурации этих объектов значение TRUE.

Однако для свойства AutomaticSerialization значение TRUE можно задать только в том случае, если все функции обратного вызова, которые требуется синхронизировать, выполняются в одном и том же IRQL. Выбор уровня выполнения, который описан далее, может привести к несовместимым уровням IRQL. В такой ситуации драйвер должен использовать блокировки платформы вместо параметра AutomaticSerialization. Дополнительные сведения о структурах конфигурации для объектов прерываний, DPC, рабочих элементов и таймеров, а также об ограничениях, применяемых к настройке Автоматическойсериализации в этих структурах, см. в разделе WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIG и WDF_TIMER_CONFIG.

Если для параметра AutomaticSerialization задано значение TRUE, следует выбрать синхронизацию на уровне очереди.

Выбор уровня выполнения

Когда драйвер создает некоторые типы объектов платформы, он может указать уровень выполнения для объекта . Уровень выполнения определяет irQL, при котором платформа будет вызывать функции обратного вызова событий объекта, обрабатывающие запросы ввода-вывода драйвера.

Если драйвер предоставляет уровень выполнения, указанный уровень влияет на функции обратного вызова для объектов очередей и файлов. Обычно, если драйвер использует автоматическую синхронизацию, платформа вызывает эти функции обратного вызова в IRQL = DISPATCH_LEVEL. Задав уровень выполнения, драйвер может заставить платформу вызывать эти функции обратного вызова в IRQL = PASSIVE_LEVEL. Платформа использует следующие правила при задании IRQL, при котором вызываются функции обратного вызова очередей и файлового объекта:

  • Если драйвер использует автоматическую синхронизацию, его функции обратного вызова очередей и файлового объекта вызываются по адресу IRQL = DISPATCH_LEVEL если драйвер не просит платформу вызвать свои функции обратного вызова в IRQL = PASSIVE_LEVEL.

  • Если драйвер не использует автоматическую синхронизацию и не задает уровень выполнения, функции обратного вызова объекта очереди драйвера и файлового объекта можно вызвать по адресу IRQL <= DISPATCH_LEVEL.

Обратите внимание, что если драйвер предоставляет функции обратного вызова файловых объектов, скорее всего, платформа будет вызывать эти функции обратного вызова в IRQL = PASSIVE_LEVEL так как некоторые данные файла, такие как имя файла, являются страничными.

Чтобы задать уровень выполнения, драйвер должен указать значение элемента ExecutionLevelструктуры WDF_OBJECT_ATTRIBUTES объекта. Ниже перечислены значения уровня выполнения, которые может указать драйвер.

WdfExecutionLevelPassive
Платформа вызывает функции обратного вызова объекта в IRQL = PASSIVE_LEVEL.

WdfExecutionLevelDispatch
Платформа может вызывать функции обратного вызова объекта в irQL <= DISPATCH_LEVEL. (Если драйвер использует автоматическую синхронизацию, платформа всегда вызывает функции обратного вызова в IRQL = DISPATCH_LEVEL.)

WdfExecutionLevelInheritFromParent
Платформа получает значение ExecutionLevel объекта из родительского объекта.

Уровень выполнения по умолчанию для объектов драйвера — WdfExecutionLevelDispatch. Уровень выполнения по умолчанию для всех остальных объектов — WdfExecutionLevelInheritFromParent.

Дополнительные сведения о значениях уровня выполнения см. в разделе WDF_EXECUTION_LEVEL.

В следующей таблице показан уровень IRQL, на котором платформа может вызывать функции обратного вызова драйвера для объектов очередей и файлов.

Область синхронизации Уровень выполнения IRQL функций обратного вызова очередей и файлов

WdfSynchronizationScopeDevice

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelPassive

PASSIVE_LEVEL

WdfSynchronizationScopeNone

WdfExecutionLevelDispatch

<= DISPATCH_LEVEL

Для драйверов, устройств, файлов, очередей, таймеров и общих объектов можно задать уровень выполнения WdfExecutionLevelPassive или WdfExecutionLevelDispatch . Для других объектов допускается только WdfExecutionLevelInheritFromParent .

Следует указать WdfExecutionLevelPassive, если:

  • Функции обратного вызова драйвера должны вызывать методы платформы или подпрограммы модели драйвера Windows (WDM), которые можно вызывать только в irQL = PASSIVE_LEVEL.

  • Функции обратного вызова драйвера должны получать доступ к коду или данным, доступным для страниц. (Например, функции обратного вызова файлового объекта обычно обращаются к страничных данным.)

Вместо того чтобы задавать WdfExecutionLevelPassive, драйвер может задать WdfExecutionLevelDispatch и предоставить функцию обратного вызова, которая создает рабочие элементы , если он должен обрабатывать некоторые операции в IRQL = PASSIVE_LEVEL.

Прежде чем решить, следует ли задать для драйвера уровень выполнения объекта WdfExecutionLevelPassive, необходимо определить IRQL, по которому вызываются драйвер и другие драйверы в стеке драйверов. Рассмотрим следующие ситуации:

  • Если драйвер находится в верхней части стека драйверов в режиме ядра, система обычно вызывает драйвер по адресу IRQL = PASSIVE_LEVEL. Клиентом такого драйвера может быть драйвер на основе UMDF или приложение в пользовательском режиме. Указание WdfExecutionLevelPassive не влияет на производительность драйвера, так как платформе не нужно ставить в очередь вызовы драйвера к рабочим элементам, которые вызываются по адресу IRQL = PASSIVE_LEVEL.

  • Если драйвер не находится в верхней части стека, система, скорее всего, не будет вызывать ваш драйвер в IRQL = PASSIVE_LEVEL. Поэтому платформа должна ставить в очередь вызовы драйвера к рабочим элементам, которые позже вызываются в IRQL = PASSIVE_LEVEL. Этот процесс может привести к снижению производительности драйвера по сравнению с тем, что функции обратного вызова драйвера могут вызываться в IRQL <= DISPATCH_LEVEL.

Для объектов DPC и объектов таймера, которые не представляют таймеры пассивного уровня, обратите внимание, что нельзя задать для элемента AutomaticSerialization структуры конфигурации значение TRUE , если для уровня выполнения родительского устройства задано значение WdfExecutionLevelPassive. Это связано с тем, что платформа получит блокировки синхронизации обратного вызова объекта устройства в IRQL = PASSIVE_LEVEL и поэтому блокировки не могут использоваться для синхронизации функций обратного вызова объектов DPC или таймера, которые должны выполняться в IRQL = DISPATCH_LEVEL. В таком случае драйвер должен использовать спиновые блокировки платформы в функциях обратного вызова объекта устройства, DPC или таймера, которые должны быть синхронизированы друг с другом.

Также обратите внимание, что для объектов таймера , представляющих таймеры пассивного уровня, можно задать для элемента AutomaticSerialization структуры конфигурации значение TRUE , только если для родительского устройства задан уровень выполнения WdfExecutionLevelPassive.