Поделиться через


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

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

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

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

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

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

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

Объект Queue

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

Объект Файл

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

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

EvtRequestCancel

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Синхронизация не выполняется

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

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

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

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

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

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

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

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

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

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

Чтобы обеспечить синхронизацию на уровне устройства для отдельных устройств, используйте значение WdfSynchronizationScopeNone по умолчанию для объекта драйвера . Задайте для 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, рабочий элемент и функции обратного вызова таймера, установите элемент автоматической сериализации в структурах конфигурации этих объектов на TRUE.

Тем не менее можно задать значение AutomaticSerialization в TRUE только в том случае, если все функции обратного вызова, которые требуется синхронизировать, выполняются на одном и том же IRQL. Выбор уровня выполнения, описанного далее, может привести к несовместимым уровням IRQL. В такой ситуации драйвер должен использовать блокировки платформы вместо настройки автоматической сериализации. Дополнительные сведения о структурах конфигурации для объектов прерываний, 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, так как некоторые данные файла, такие как имя файла, доступны для страницы.

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

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

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

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

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

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

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

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

WdfSynchronizationScopeDevice

WdfExecutionLevelPassive

пассивный уровень

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeQueue

WdfExecutionLevelPassive

пассивный уровень

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

Уровень диспетчеризации

WdfSynchronizationScopeNone

WdfExecutionLevelPassive

пассивный уровень

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.