フレームワーク作業項目の使用

作業項目とは、ドライバーが EvtWorkItem イベント コールバック関数で実行するタスクです。 これらの関数は、システム ワーカー スレッドのコンテキストで、IRQL = PASSIVE_LEVEL で非同期的に実行されます。

フレームワークベースのドライバーは、通常、IRQL = DISPATCH_LEVEL で実行される EvtInterruptDpc または EvtDpcFunc 関数が IRQL = PASSIVE_LEVELで追加処理を実行する必要がある場合に、作業項目を使用します。

つまり、IRQL = DISPATCH_LEVEL で実行される関数が、IRQL = PASSIVE_LEVEL でのみ呼び出すことができる関数を呼び出す必要がある場合、ドライバーは作業項目を使用できます。

通常、ドライバーの EvtInterruptDpc コールバック関数または EvtDpcFunc コールバック関数は、作業項目オブジェクトを作成し、システムの作業項目キューに追加します。 その後、システム ワーカー スレッドはオブジェクトをデキューし、作業項目の EvtWorkItem コールバック関数を呼び出します。

作業項目を使用するサンプル ドライバー

作業項目を使用するフレームワークベースのドライバーの例としては、1394、AMCC5933、PCIDRV、トースターなどがあります。

作業項目のセットアップ

作業項目のセットアップには、ドライバーで次の操作を行う必要があります。

  1. 作業項目を作成する。

    ドライバーは WdfWorkItemCreate を呼び出して作業項目オブジェクトを作成し、作業項目を処理する EvtWorkItem コールバック関数を識別します。

  2. 作業項目に関する情報を格納します。

    通常、ドライバーは、作業項目オブジェクトのコンテキスト メモリを使用して、EvtWorkItem コールバック関数が実行する必要があるタスクに関する情報を格納します。 EvtWorkItem コールバック関数が呼び出されると、このコンテキスト メモリにアクセスして情報を取得できます。 コンテキスト メモリを割り当ててアクセスする方法については、「フレームワーク オブジェクト コンテキスト空間」を参照してください。

  3. 作業項目をシステムの作業項目キューに追加します。

    ドライバーは WdfWorkItemEnqueue を呼び出します。これによって、ドライバーの作業項目が作業項目キューに追加されます。

ドライバーが WdfWorkItemCreate を呼び出す場合、フレームワーク デバイス オブジェクトまたはフレームワーク キュー オブジェクトへのハンドルを提供する必要があります。 そのオブジェクトが削除されると、そのオブジェクトに関連付けられている既存の作業項目も削除されます。 作業項目オブジェクトは破棄され、親オブジェクトの EvtCleanupCallback コールバックが呼び出される前に、関連する作業項目コールバックがクリーンアップされます。

フレームワーク オブジェクト階層のクリーンアップの規則の詳細については、「フレームワーク オブジェクトのライフ サイクル」を参照してください。

作業項目コールバック関数の使用

作業項目キューに作業項目が追加されると、システム ワーカー スレッドが使用可能になるまでキューに残ります。 システム ワーカー スレッドは、キューから作業項目を削除し、ドライバーの EvtWorkItem コールバック関数を呼び出し、作業項目オブジェクトを入力として渡します。

通常、EvtWorkItem コールバック関数は次の手順を実行します。

  1. 作業項目オブジェクトのコンテキスト メモリにアクセスして、作業項目に関するドライバー指定の情報を取得します。

  2. 指定したタスクを実行します。 必要に応じて、作業項目の親オブジェクトを決定するために、コールバック関数は WdfWorkItemGetParentObject を呼び出すことができます。

  3. WdfObjectDelete を呼び出して作業項目オブジェクトを削除するか、ドライバーが作業項目を再度キューに入れる場合は、作業項目へのハンドルが再利用可能になったことを示します。

各作業項目のコールバック関数が実行するタスクは、比較的短い必要があります。 オペレーティング システムに用意されているシステム ワーカー スレッドの数は限られているため、作業項目コールバック関数を使用して時間のかかるタスクを実行すると、ドライバーによってシステムのパフォーマンスが低下する可能性があります。

作業項目の作成と削除

ドライバーは、次の 2 つの手法のいずれかを使用して、作業項目を作成および削除できます。

  • 各作業項目を 1 回使用します。必要なときに作業項目を作成し、使用した直後に削除します。

    この手法は、少数の作業項目の低い頻度で使用 (1 分に 1 回未満) する必要のあるドライバーに適しています。

    たとえば、ドライバーの EvtInterruptDpc コールバック関数は、WdfWorkItemCreateWdfWorkItemEnqueue を呼び出し、次に作業項目の EvtWorkItem コールバック関数は WdfObjectDelete を呼び出すことができます。

    ドライバーがこのシナリオに従い、その EvtInterruptDpc コールバック関数が WdfWorkItemCreate から戻り値 STATUS_INSUFFICIENT_RESOURCES を受け取った場合、ドライバーはシステム リソース (通常はメモリ) が使用可能になるまで必要な作業を延期できる必要があります。

  • ドライバーが必要に応じてキューに入れ直す 1 つ以上の作業項目を作成します。

    この手法は、作業項目を頻繁に使用するドライバー (1 分あたり 1 回以上) や、ドライバーの EvtInterruptDpc コールバック関数が WdfWorkItemCreate からの戻り値 STATUS_INSUFFICIENT_RESOURCES を簡単に処理できない場合に便利です。

    ドライバーが WdfWorkItemEnqueue を呼び出すまで、システムは作業項目にワーカー スレッドを割り当てません。 そのため、システム ワーカー スレッドは限られたリソースですが、デバイスの初期化中に作業項目を作成すると少量のメモリが消費されますが、それ以外の場合はシステムのパフォーマンスには影響しません。

    次のステップでは、考えられるシナリオについて説明します。

    1. ドライバーの EvtDriverDeviceAdd コールバック関数は、WdfWorkItemCreate を呼び出して作業項目ハンドルを取得します。
    2. ドライバーの EvtInterruptDpc コールバック関数は、EvtWorkItem コールバック関数が実行する必要があるアクションの一覧を作成し、手順 1 のハンドルを使用して WdfWorkItemEnqueue を呼び出します。
    3. ドライバーの EvtWorkItem コールバック関数は、アクションの一覧を実行し、コールバック関数が実行されたことを示すフラグを設定します。

    その後、ドライバーの EvtInterruptDpc コールバック関数が呼び出されるたびに、EvtWorkItem コールバック関数が実行されているかどうかを判断する必要があります。 EvtWorkItem コールバック関数が実行されていない場合、作業項目がまだキューに登録されているため、EvtInterruptDpc コールバック関数は WdfWorkItemEnqueue を呼び出しません。 この場合、EvtInterruptDpc コールバック関数は、EvtWorkItem コールバック関数のアクションの一覧のみを更新します。

    各作業項目は、デバイスまたはキューに関連付けられます。 関連付けられているデバイスまたはキューが削除されると、フレームワークは関連付けられているすべての作業項目を削除するため、この手法を使用している場合、ドライバーは WdfObjectDelete を呼び出す必要はありません。

いくつかのドライバーは、作業項目キューから作業項目をフラッシュするために WdfWorkItemFlush を呼び出す必要がある場合があります。 WdfWorkItemFlush の使用例については、メソッドのリファレンス ページを参照してください。

ドライバーが未処理の作業項目に対して WdfObjectDelete を呼び出す場合、結果は作業項目の状態によって異なります。

作業項目の状態 結果
作成されたがエンキューされていない 作業項目はすぐにクリーンアップされます。
エンキュー化 WdfObjectDelete の呼び出しは、作業項目の実行が完了するまで待機し、作業項目がクリーンアップされます
実行中 ドライバーが EvtWorkItem (同じスレッド) 内から WdfObjectDelete を呼び出すと、WdfObjectDelete は直ちに返されます。 EvtWorkItem が完了すると、作業項目がクリーンアップされます。 それ以外の場合、WdfObjectDelete は EvtWorkItem の完了を待機します。