共用方式為


定義和使用事件物件

任何使用事件對象的驅動程式都必須在等候、設定、清除或重設事件之前,先呼叫 KeInitializeEventIoCreateNotificationEventIoCreateSynchronizationEvent 。 下圖說明具有線程的驅動程式如何使用事件對象進行同步處理。

圖表說明正在等候事件物件。

如上圖所示,這類驅動程式必須提供事件對象的記憶體,該對象必須是常駐。 驅動程式可以使用其所建立的裝置物件的裝置延伸功能,如果使用了控制器物件,則可使用控制器延伸,或者使用驅動程式配置的非分頁集區。

當驅動程式呼叫 KeInitializeEvent 時,它必須傳遞事件對象的驅動程式常駐記憶體指標。 此外,呼叫端必須指定事件物件的初始狀態(已發出訊號或未發出訊號)。 呼叫端也必須指定事件類型,這可以是下列其中一項:

  • SynchronizationEvent

    同步處理事件 設定為已觸發狀態時,正在等候事件重設成 Not-Signaled 的單一線程會符合執行資格,且事件的狀態會自動重設為未觸發。

    這種事件有時稱為 自動清除事件,因為其 Signaled 狀態會在每次滿足等候時自動重設。

  • NotificationEvent

    通知事件設定為 Signaled 狀態時,等候事件重設到 Not-Signaled 的所有線程都有資格執行,而且事件會維持在 Signaled 狀態,直到明確重設為 Not-Signaled 發生為止:也就是說,會使用指定的事件指標呼叫 KeClearEventKeResetEvent

很少有裝置或中繼驅動程式具有單一驅動程式專用線程,更不用說一組線程,藉由等候保護共用資源的事件來同步處理其作業。

大部分使用事件物件等候 I/O 作業完成的驅動程式會在呼叫 KeInitializeEvent 時,將輸入 Type 設定為 NotificationEvent。 為驅動程式使用 IoBuildSynchronousFsdRequestIoBuildDeviceIoControlRequest 所建立的 IRP 所設定的事件對象,幾乎一律會初始化為 NotificationEvent ,因為呼叫端會等候事件的通知,指出其要求已由一或多個較低層級的驅動程式滿足。

在驅動程式初始化後,如果有專用線程,則此線程及其他例程可以在事件上同步操作。 例如,具有管理 IRP 佇列之線程的驅動程式,例如系統軟盤控制器驅動程式,可能會在事件上同步處理 IRP,如前圖所示。

  1. 線程將一個 IRP 從佇列中取出,以便在裝置上進行處理,並使用指向驅動程式所提供的初始化事件對象儲存空間的指標來呼叫 KeWaitForSingleObject

  2. 其他驅動程式例程會執行滿足 IRP 所需的 I/O 作業,而且當這些作業完成時,驅動程式的 DpcForIsr 例程會使用事件物件的指標呼叫 KeSetEvent 、線程的驅動程式決定優先順序提升(遞增,如上圖所示),以及 Boolean Wait 設定為 FALSE。 呼叫 KeSetEvent 會將事件物件設定為 Signaled 狀態,藉此將等候線程的狀態變更為就緒。

  3. 核心會在處理器可供使用時立即分派執行線程:也就是說,目前沒有其他優先順序較高的線程處於就緒狀態,而且沒有任何核心模式例程可在較高的 IRQL 執行。

    如果 DpcForIsr 尚未針對 IRP 呼叫 IoCompleteRequest,線程現在可以完成該 IRP,並可以從佇列中取出另一個 IRP 在裝置上進行處理。

呼叫 KeSetEvent 並將 Wait 參數設為 TRUE 表示呼叫端打算立即呼叫 KeWaitForSingleObjectKeWaitForMultipleObjects 支援 從 KeSetEvent 傳回的例程。

請考慮下列指導方針,將Wait參數設定為KeSetEvent:

在 IRQL < DISPATCH_LEVEL 執行的可分頁線程或可分頁驅動程式例程,絕不應呼叫 KeSetEvent 且不應將 Wait 參數設定為 TRUE。 如果呼叫端碰巧在呼叫 KeSetEventKeWaitForSingleObjectKeWaitForMultipleObjects 之間被分頁,就會造成致命頁面錯誤。

任何在 IRQL = DISPATCH_LEVEL 執行的標準驅動程式例程,都無法等候任何分派器物件的非零間隔,否則會導致系統崩潰。 不過,這類例程可以在執行於 IRQL 小於或等於 DISPATCH_LEVEL 時,呼叫 KeSetEvent

如需標準驅動程式例程執行之 IRQL 的摘要,請參閱 管理硬體優先順序

KeResetEvent 會傳回指定事件的先前狀態,即在呼叫KeResetEvent時,它是否已被設定為「已發訊號」狀態。 KeClearEvent 只會將指定 事件 的狀態設定為 Not-Signaled。

針對何時呼叫上述支援例程,請考慮下列指導方針:

為了獲得更好的效能,除非呼叫端需要 KeResetEvent 傳回的信息,否則每個驅動程式都應該呼叫 KeClearEvent,以判斷接下來要執行的動作。