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

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

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

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

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

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

作業項目を使用するフレームワークベース ドライバーのサンプルには、1394、AMCC5933、PCIDRV、および Toaster があります。

作業項目の設定

作業項目を設定するために、ドライバーは次の処理を行う必要があります。

  1. 作業項目を作成します。

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

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

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

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

    ドライバーは WdfWorkItemEnqueue を呼び出します。このメソッドは、ドライバーの作業項目を作業項目キューに追加します。

ドライバーは WdfWorkItemCreate を呼び出すときに、フレームワーク デバイス オブジェクトまたはフレームワーク キュー オブジェクトへのハンドルを指定する必要があります。そのオブジェクトが削除されると、オブジェクトに関連付けられた既存の作業項目も削除されます。

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

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

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

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

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

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

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

作業項目の作成および削除

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

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

    この方法は、少数の作業項目をまれに使用する (1 分に 1 回未満の頻度で使用する) 必要がある場合に便利です。

    たとえば、ドライバーの EvtInterruptDpc コールバック関数は WdfWorkItemCreate を呼び出した後、WdfWorkItemEnqueue を呼び出し、作業項目の 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 の使用例については、メソッドのリファレンス ページを参照してください。