次の方法で共有


システム ワーカー スレッド

遅延処理を必要とするドライバーは、実際の処理を実行するドライバー コールバック ルーチンへのポインターを含む作業項目を使用できます。 ドライバーは作業項目をキューに入れ、システム ワーカー スレッド がキューから作業項目を削除し、ドライバーのコールバック ルーチンを実行します。 システムはこれらのシステム ワーカー スレッドのプールを維持します。これは、各システム スレッドが一度に 1 つの作業項目を処理するシステム スレッドです。

ドライバーは、WorkItem コールバック ルーチンを作業項目に関連付けます。 システム ワーカー スレッドは、作業項目を処理するときに、関連付けられている WorkItem ルーチンを呼び出します。 Windows Vista 以降のバージョンの Windows では、ドライバーは代わりに WorkItemEx ルーチンを作業項目に関連付けることができます。 WorkItemEx は、WorkItem が受け取るパラメーターとは異なるパラメーターを受け取ります。

WorkItem ルーチンと WorkItemEx ルーチンは、システム スレッド コンテキストで実行されます。 ドライバー ディスパッチ ルーチンがユーザー モードのスレッド コンテキストで実行できる場合、そのルーチンは、WorkItem ルーチンまたは WorkItemEx ルーチンを呼び出して、システム スレッド コンテキストを必要とする操作を実行できます。

作業項目を使用するため、ドライバーは次の手順を実行します。

  1. 新しい作業項目を割り当てて初期化します。

    システムは、IO_WORKITEM 構造を使用して作業項目を保持します。 新しい IO_WORKITEM 構造体を割り当てて作業項目として初期化するため、ドライバーは IoAllocateWorkItem を呼び出すことができます。 Windows Vista 以降のバージョンの Windows では、ドライバーは独自の IO_WORKITEM 構造体を割り当て、IoInitializeWorkItem を呼び出して構造体を作業項目として初期化することもできます。 (作業項目を保持するために必要なバイト数を決定するため、ドライバーは IoSizeofWorkItem を呼び出す必要があります)。

  2. コールバック ルーチンを作業項目に関連付け、システム ワーカー スレッドによって処理されるように作業項目をキューに入れます。

    WorkItem ルーチンを作業項目に関連付け、作業項目をキューに登録するため、ドライバーは IoQueueWorkItem を呼び出す必要があります。 一方、WorkItemEx ルーチンを作業項目に関連付け、作業項目をキューに登録するため、ドライバーは IoQueueWorkItemEx を呼び出す必要があります。

  3. 作業項目が不要になった後は、解放します。

    IoAllocateWorkItem によって割り当てられた作業項目は、IoFreeWorkItem によって解放する必要があります。 IoInitializeWorkItem によって初期化された作業項目は、解放する前に IoUninitializeWorkItem によって未初期化されている必要があります。

    作業項目は、作業項目が現在キューに登録されていない場合にのみ初期化または解放できます。 システムは、作業項目のコールバック ルーチンを呼び出す前に作業項目をデキューするため、コールバック内から IoFreeWorkItemIoUninitializeWorkItem を呼び出すことができます。

時間の長い処理を必要とする処理タスクを開始する必要がある DPC、またはブロック呼び出しを行う DPC は、そのタスクの処理を 1 つ以上の作業項目に委任する必要があります。 DPC の実行中は、すべてのスレッドが実行されません。 さらに、IRQL = DISPATCH_LEVEL で実行される DPC は、ブロック呼び出しを行うことはできません。 ただし、作業項目を処理するシステム ワーカー スレッドは IRQL = PASSIVE_LEVEL で実行されます。 したがって、作業項目にはブロック呼び出しを含めることができます。 たとえば、システム ワーカー スレッドはディスパッチャー オブジェクトを待機できます。

システム ワーカー スレッドのプールは限られたリソースであるため、 WorkItem ルーチンと WorkItemEx ルーチンは、短時間かかる操作にのみ使用できます。 これらのルーチンの 1 つが実行時間が長すぎる場合 (無期限のループが含まれている場合など)、または長時間待機する場合は、システムがデッドロックする可能性があります。 そのため、ドライバーが長時間の遅延処理を必要とする場合は、代わりに PsCreateSystemThread を呼び出して独自のシステム スレッドを作成する必要があります。

IoQueueWorkItem または IoQueueWorkItemEx を呼び出して、既にキューに入っている作業項目をキューに入れることはできません。 これにより、システム データ構造が破損する可能性があります。 特定のドライバー ルーチンが実行されるたびにドライバーが同じ作業項目をキューに入れる場合は、次の手法を使用して、作業項目が既にキューに入っている場合に 再度キューに入れないようにすることができます。

  • ドライバーはワーカー ルーチンのタスク リストを維持します。
  • このタスク リストは、ワーカー ルーチンに提供されるコンテキストで使用できます。 ワーカー ルーチンと、タスク リストを変更するドライバー ルーチンは、リストへのアクセスを同期します。
  • ワーカー ルーチンは、実行するたびにリスト内のすべてのタスクを実行し、タスクの完了時に各タスクをリストから削除します。
  • 新しいタスクが到着すると、ドライバーはこのタスクをリストに追加します。 ドライバーは、タスク リストが以前に空であった場合にのみ、作業項目をキューに入れます。

システム ワーカー スレッドは、ワーカー スレッドを呼び出す前に、キューから作業項目を削除します。 したがって、ドライバー スレッドは、ワーカー スレッドの実行が開始されるとすぐに、作業項目を安全に再びキューに登録できます。