次の方法で共有


自動同期の使用

フレームワーク ベースのドライバーのほぼすべてのコードは、イベント コールバック関数に存在します。 フレームワークは、次のように、ドライバーのコールバック関数の大部分を自動的に同期します。

フレームワークでは、内部同期ロックのセットを使用して、この自動同期を実装します。 フレームワークにより、2 つ以上のスレッドが同じコールバック関数を同時に呼び出すことはできません。 各スレッドは、コールバック関数を呼び出す前に同期ロックを取得できるようになるまで待機する必要があります。 (必要に応じて、ドライバーは必要に応じてこれらの同期ロックを取得することもできます。詳細については、「 フレームワーク ロックの使用」を参照してください)。

ドライバーは、オブジェクトコンテキスト 空間にオブジェクト固有のデータを格納する必要があります。 ドライバーがフレームワークで定義されたインターフェイスのみを使用する場合、オブジェクトへのハンドルを受け取るコールバック関数のみがこのデータにアクセスできます。 フレームワークがドライバーのコールバック関数の呼び出しを同期している場合、一度に呼び出されるコールバック関数は 1 つだけです。 オブジェクトのコンテキスト空間には、一度に 1 つのコールバック関数にのみアクセスできます。

ドライバーが パッシブ レベルの割り込み処理を実装しない限り、割り込みおよび割り込みデータへのアクセスを処理するコードは、デバイスの IRQL (DIRQL) で実行する必要があり、追加の同期が必要です。 詳細については、「 割り込みコードの同期」を参照してください。

ドライバーが I/O 要求を処理するコールバック関数の自動同期を有効にする場合、フレームワークはこれらのコールバック関数を同期して、一度に 1 つずつ実行します。 次の表に、フレームワークが同期するコールバック関数の一覧を示します。

オブジェクトの種類 同期されたコールバック関数

Queue オブジェクト

要求ハンドラーEvtIoQueueStateEvtIoResumeEvtIoStop

File オブジェクト

すべての コールバック関数

Request オブジェクト

EvtRequestCancel

必要に応じて、フレームワークは、ドライバーがデバイスに提供する割り込み、DPC、作業項目、およびタイマー オブジェクトのコールバック関数 (割り込みオブジェクトの EvtInterruptIsr コールバック関数を除く) とこれらのコールバック関数を同期することもできます。 この追加の同期を有効にするには、ドライバーは、これらのオブジェクトの構成構造の AutomaticSerialization メンバーを TRUE に設定する必要があります。

要約すると、フレームワークの自動同期機能には、次の機能があります。

  • フレームワークは、常に各デバイスの PnP と電源管理コールバック関数を同期します。

  • 必要に応じて、フレームワークは I/O キューの要求ハンドラーといくつかの追加のコールバック関数を同期できます (前の表を参照)。

  • ドライバーは、割り込み、DPC、作業項目、およびタイマー オブジェクトのコールバック関数を同期するようにフレームワークに要求できます。

  • ドライバーは、「割り込みコードの同期」で説明されている手法を用いて、割り込みを処理し、割り込みデータにアクセスするコードを同期する必要があります。

  • フレームワークは、ドライバーの CompletionRoutine コールバック関数や I/O ターゲット オブジェクトが定義するコールバック関数など、ドライバーの他のコールバック関数を同期しません。 代わりに、フレームワークは、ドライバーがこれらのコールバック関数を同期するために使用できる追加 のロック を提供します。

同期スコープの選択

フレームワークで、デバイスのすべての I/O キューに関連付けられているすべてのコールバック関数を同期するように選択できます。 または、デバイスの I/O キューごとに、フレームワークでコールバック関数を個別に同期するように選択することもできます。 ドライバーで使用できる同期オプションは次のとおりです。

  • デバイス レベルの同期

    フレームワークは、デバイスのすべての I/O キューのコールバック関数を同期するため、一度に 1 つずつ実行されます。 フレームワークは、コールバック関数を呼び出す前にデバイスの同期ロックを取得することで、この同期を実現します。

  • キュー レベルの同期

    フレームワークは、個々の I/O キューごとにコールバック関数を同期するため、一度に 1 つずつ実行されます。 フレームワークは、コールバック関数を呼び出す前にキューの同期ロックを取得することで、この同期を実現します。

  • 同期なし

    フレームワークは、前の表のコールバック関数の実行を同期せず、コールバック関数を呼び出す前に同期ロックを取得しません。 同期が必要な場合は、ドライバーが指定する必要があります。

フレームワークがデバイス レベルの同期、キュー レベルの同期、または同期なしをドライバーに提供するかを指定するには、ドライバー オブジェクト、デバイス オブジェクト、またはキュー オブジェクトの同期スコープを指定します。 オブジェクトのWDF_OBJECT_ATTRIBUTES構造体の SynchronizationScope メンバーは、オブジェクトの同期スコープを識別します。 ドライバーで指定できる同期スコープの値は次のとおりです。

WdfSynchronizationScopeDevice
フレームワークは、デバイス オブジェクトの同期ロックを取得して同期します。

WdfSynchronizationScopeQueue
フレームワークは、キュー オブジェクトの同期ロックを取得して同期します。

WdfSynchronizationScopeNone
フレームワークは同期せず、同期ロックを取得しません。

WdfSynchronizationScopeInheritFromParent
フレームワークは、オブジェクトの親オブジェクトからオブジェクトの SynchronizationScope 値を取得します。

一般に、デバイス レベルの同期は使用しないでください。

同期スコープの値の詳細については、「 WDF_SYNCHRONIZATION_SCOPE」を参照してください。

ドライバー オブジェクトの既定の同期スコープは WdfSynchronizationScopeNone です。 デバイス オブジェクトとキュー オブジェクトの既定の同期スコープは WdfSynchronizationScopeInheritFromParent です

フレームワークを使用してすべてのデバイスにデバイス レベルの同期を提供するには、ドライバーのドライバー オブジェクトのWDF_OBJECT_ATTRIBUTES構造で SynchronizationScopeWdfSynchronizationScopeDevice に設定します。 各デバイス オブジェクトの既定の WdfSynchronizationScopeInheritFromParent 値を使用します。

個々のデバイスにデバイス レベルの同期を提供するには、ドライバー オブジェクトの既定の WdfSynchronizationScopeNone 値を使用します。 個々のデバイス オブジェクトのWDF_OBJECT_ATTRIBUTES構造で SynchronizationScopeWdfSynchronizationScopeDevice に設定します。

フレームワークでデバイスのキュー レベルの同期を提供する場合は、次の手法を使用できます。

  • フレームワーク バージョン 1.9 以降では、キュー オブジェクトのWDF_OBJECT_ATTRIBUTES構造で WdfSynchronizationScopeQueue を設定することで、個々のキューのキュー レベルの同期を有効にします。 この手法が好ましい。

  • または、すべてのフレームワーク バージョンで次の手順を使用できます。

    1. デバイス オブジェクトのWDF_OBJECT_ATTRIBUTES構造で SynchronizationScopeWdfSynchronizationScopeQueue に設定します。
    2. 各デバイスのキュー オブジェクトには、既定の WdfSynchronizationScopeInheritFromParent 値を使用します。

フレームワークでドライバーの I/O 要求を処理するコールバック関数を同期させたくない場合は、ドライバーのドライバー、デバイス、キュー オブジェクトの既定の SynchronizationScope 値を使用します。 この場合、フレームワークはドライバーの I/O 要求関連のコールバック関数を自動的に同期しません。 フレームワークは、IRQL <= DISPATCH_LEVELでコールバック関数を呼び出すことができます。

SynchronizationScope 値を設定すると、前のテーブルに含まれているコールバック関数のみが同期されます。 フレームワークでドライバーの割り込み、DPC、作業項目、タイマー オブジェクトのコールバック関数も同期させる場合は、これらのオブジェクトの構成構造の AutomaticSerialization メンバーを TRUE に設定します。

ただし、 AutomaticSerializationTRUE に設定できるのは、同期するすべてのコールバック関数が同じ IRQL で実行される場合のみです。 次に説明する 実行レベルを選択すると、互換性のない IRQL レベルになる可能性があります。 このような状況では、ドライバーは、AutomaticSerialization を設定する代わりにフレームワーク ロックを使用する必要があります。 割り込み、DPC、作業項目、およびタイマー オブジェクトの構成構造の詳細、およびこれらの構造体での AutomaticSerialization の設定に適用される制限の詳細については、 WDF_INTERRUPT_CONFIGWDF_DPC_CONFIGWDF_WORKITEM_CONFIG、および WDF_TIMER_CONFIGを参照してください。

AutomaticSerializationTRUE に設定した場合は、キュー レベルの同期を選択します。

実行レベルの選択

ドライバーは、いくつかの種類のフレームワーク オブジェクトを作成するときに、オブジェクトの 実行レベル を指定できます。 実行レベルでは、フレームワークがドライバーの I/O 要求を処理するオブジェクトのイベント コールバック関数を呼び出す IRQL を指定します。

ドライバーが実行レベルを提供する場合、指定されたレベルは、キューおよびファイル オブジェクトのコールバック関数に影響します。 通常、ドライバーが自動同期を使用する場合、フレームワークは IRQL = DISPATCH_LEVELでこれらのコールバック関数を呼び出します。 実行レベルを指定することで、ドライバーは IRQL = PASSIVE_LEVELでこれらのコールバック関数を呼び出すフレームワークを強制できます。 フレームワークは、キューとファイル オブジェクトのコールバック関数を呼び出す IRQL を設定するときに、次の規則を使用します。

  • ドライバーが自動同期を使用する場合、フレームワークは IRQL = DISPATCH_LEVELでキューとファイル オブジェクトのコールバック関数を呼び出します。ただし、ドライバーが IRQL = PASSIVE_LEVEL でコールバック関数を呼び出すようにフレームワークに要求しない限りです。

  • ドライバーが自動同期を使用せず、実行レベルを指定しない場合、フレームワークは IRQL <= DISPATCH_LEVELでドライバーのキューとファイル オブジェクトのコールバック関数を呼び出すことができます。

ドライバーがファイル オブジェクトコールバック関数を提供する場合、ほとんどの場合、ファイル名などの一部のファイル データはページング可能であるため、フレームワークで IRQL = PASSIVE_LEVEL でこれらのコールバック関数を呼び出す必要があります。

実行レベルを指定するには、ドライバーは、オブジェクトのWDF_OBJECT_ATTRIBUTES構造体の ExecutionLevel メンバーの値を指定する必要があります。 ドライバーで指定できる実行レベルの値は次のとおりです。

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 を指定します。

  • ドライバーのコールバック関数は、IRQL = PASSIVE_LEVELでのみ呼び出すことができるフレームワーク メソッドまたは Windows ドライバー モデル (WDM) ルーチンを呼び出す必要があります。

  • ドライバーのコールバック関数は、ページング可能なコードまたはデータにアクセスする必要があります。 たとえば、ファイル オブジェクトコールバック関数は通常、ページング可能なデータにアクセスします。

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 オブジェクトの場合、およびパッシブ レベルのタイマーを表さないタイマー オブジェクトの場合、親デバイスの実行レベルを WdfExecutionLevelPassive に設定した場合、構成構造の AutomaticSerialization メンバーを TRUE に設定することはできません。 フレームワークは、IRQL = PASSIVE_LEVEL でデバイス オブジェクトの コールバック同期ロック を取得します。 そのため、ロックを使用して DPC またはタイマー オブジェクトのコールバック関数を同期することはできません。これは IRQL = DISPATCH_LEVELで実行する必要があります。 この場合、ドライバーは、相互に同期する必要がある任意のデバイス、DPC、またはタイマー オブジェクトのコールバック関数で フレームワーク スピン ロック を使用する必要があります。

また、パッシブ レベルのタイマーを表すタイマー オブジェクトの場合は、親デバイスの実行レベルが WdfExecutionLevelPassive に設定されている場合にのみ、構成構造の AutomaticSerialization メンバーを TRUE に設定できます。