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

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

イベント オブジェクトの待機を示す図。

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

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

  • SynchronizationEvent

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

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

  • NotificationEvent

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

デバイスまたは中間ドライバーの中には、共有リソースを保護するイベントを待機することによって操作を同期する可能性があるスレッドのセットはもちろん、ドライバー専用スレッドが 1 つ存在するドライバーはほとんどありません。

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

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

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

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

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

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

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

WaitパラメーターをKeSetEvent に設定するには、次のガイドラインを考慮してください。

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

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

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

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

前のサポート ルーチンを呼び出すタイミングについては、次のガイドラインを考慮してください。

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