イベント オブジェクトの定義と使用

イベント オブジェクトを使用するすべてのドライバーは、イベントを待機、設定、クリア、またはリセットする前に、 KeInitializeEventIoCreateNotificationEvent、または IoCreateSynchronizationEvent を呼び出す必要があります。 次の図は、スレッドを持つドライバーが同期にイベント オブジェクトを使用する方法を示しています。

diagram illustrating waiting for an event object.

前の図に示すように、このようなドライバーは、イベント オブジェクトのストレージを提供する必要があります。これは常駐している必要があります。 ドライバーは、ドライバーによって作成されたデバイス オブジェクトの デバイス拡張機能 、コントローラー オブジェクトを使用する場合は コントローラー拡張機能、またはドライバーによって割り当てられた非ページ プールを使用できます。

ドライバーが KeInitializeEvent を呼び出すときは、イベント オブジェクトのドライバーの常駐ストレージへのポインターを渡す必要があります。 さらに、呼び出し元はイベント オブジェクトの初期状態 (シグナルまたはシグナルなし) を指定する必要があります。 呼び出し元はイベントの種類も指定する必要があります。イベントの種類は次のいずれかになります。

  • SynchronizationEvent

    同期イベントが Signaled 状態に設定されている場合、イベントが Not-Signaled にリセットされるのを待機している 1 つのスレッドが実行の対象となり、イベントの状態が自動的に Not-Signaled にリセットされます。

    この種のイベントは 自動クリア イベントと呼ばれることもあります。これは、待機が満たされるたびに Signaled 状態が自動的にリセットされるためです。

  • NotificationEvent

    通知イベントが Signaled 状態に設定されている場合、イベントがNot-Signaledにリセットされるのを待っていたすべてのスレッドが実行の対象となり、イベントは、Not-Signaledへの明示的なリセットが発生するまで Signaled 状態のままです。つまり、指定されたイベント ポインターを使用して KeClearEvent または KeResetEvent を呼び出します。

デバイスまたは中間ドライバーの中には、単一のドライバー専用スレッドがあります。つまり、共有リソースを保護するイベントを待機することで操作を同期する可能性のあるスレッドのセットは含まれません。

イベント オブジェクトを使用して I/O 操作の完了を待機するほとんどのドライバーは、KeInitializeEvent を呼び出すときに入力の種類NotificationEvent に設定します。 ドライバーが IoBuildSynchronousFsdRequest または IoBuildDeviceIoControlRequest を使用して作成する IRP 用に設定されたイベント オブジェクトは、呼び出し元が 1 つ以上の下位レベルのドライバーによって要求が満たされたことを通知するイベントを待機するため、ほとんどの場合、NotificationEvent として初期化されます。

ドライバー自体が初期化されると、そのドライバー専用スレッド (存在する場合) やその他のルーチンは、イベントに対する操作を同期できます。 たとえば、システム フロッピー コントローラー ドライバーなどの Irp のキューを管理するスレッドを持つドライバーは、前の図に示すように、イベントの IRP 処理を同期する可能性があります。

  1. デバイスで処理するために IRP をデキューしたスレッドは、初期化されたイベント オブジェクトのドライバー提供の記憶域へのポインターを使用して KeWaitForSingleObject を呼び出します。

  2. その他のドライバー ルーチンは、IRP を満たすために必要な I/O 操作をデバイスを実行し、これらの操作が完了すると、ドライバーの DpcForIsr ルーチンは、イベント オブジェクトへのポインター、スレッドのドライバーによって決定された優先度ブースト (前の図に示すようにインクリメント)、ブール値待機FALSE に設定して KeSetEvent を呼び出します。 KeSetEvent を呼び出すと、イベント オブジェクトが Signaled 状態に設定され、待機中のスレッドの状態が準備完了に変更されます。

  3. カーネルは、プロセッサが使用可能になるとすぐに実行のためにスレッドをディスパッチします。つまり、優先度が高い他のスレッドは現在準備完了状態であり、より高い IRQL で実行するカーネル モード ルーチンはありません。

    DpcForIsr が既に IRP で IoCompleteRequest を呼び出していない場合、スレッドは IRP を完了できるようになり、デバイスで処理される別の IRP をデキューできます。

Wait パラメーターを TRUE に設定して KeSetEvent を呼び出すと、呼び出し元は、KeSetEvent からの戻り時に KeWaitForSingleObject または KeWaitForMultipleObjects サポート ルーチンを直ちに呼び出す意図を示します。

Waitparameter toKeSetEventを設定するための次のガイドラインを検討してください

IRQL < DISPATCH_LEVELで実行されるページング可能なスレッドまたはページング可能なドライバー ルーチンは、Wait パラメーターが TRUE に設定された KeSetEvent を呼び出さないでください。 このような呼び出しは、呼び出し元が KeSetEventKeWaitForSingleObject または KeWaitForMultipleObjects への呼び出しの間にページングされた場合に、致命的なページ エラーを引き起こします。

IRQL = DISPATCH_LEVELで実行される標準ドライバー ルーチンは、システムをダウンさせずに、ディスパッチャー オブジェクトで 0 以外の間隔を待つことはできません。 ただし、このようなルーチンは、DISPATCH_LEVEL以下の IRQL で実行中に KeSetEvent を呼び出すことができます。

標準ドライバー ルーチンが実行される IRQL の概要については、「 ハードウェア優先度の管理」を参照してください。

KeResetEvent は 、指定された イベントの前の状態 ( KeResetEvent の呼び出しが発生したときに Signaled に設定されたかどうか) を返します。 KeClearEvent は 、指定された イベント の状態を Not-Signaled に設定するだけです。

上記のサポート ルーチンを呼び出す場合は、次のガイドラインを検討してください。

パフォーマンスを向上させるには、呼び出し元が次に何を行うかを決定するために KeResetEvent によって返される情報が必要でない限り、すべてのドライバーが KeClearEvent を呼び出す必要があります。