處理 Lowest-Level 驅動程式中的 IRP
最低層級實體驅動程式具有較高層級驅動程式不需要的特定標準常式。 最低層級驅動程式的標準常式集合也會根據下列準則而有所不同:
每個驅動程式控制項的裝置本質
驅動程式是否為直接或緩衝 I/O 設定其裝置物件
個別驅動程式的設計
為了說明標準驅動程式常式的角色,下圖顯示 IRP 範例可能採用的路徑,因為 IRP 是由最低層級的大量儲存設備磁碟機處理。 圖中的驅動程式具有下列特性:
裝置會在每個 I/O 作業結束時產生中斷,因此此驅動程式具有 ISR 和 DpcForIsr 常式。
驅動程式具有 StartIo 常式,而不是設定 IRP 的內部佇列,以及管理自己的佇列。
驅動程式會使用系統 DMA,因此它會設定其裝置物件的直接 I/O 旗 標,並具有 AdapterControl 常式。
如圖所示,I/O 管理員會建立 IRP,並將它傳送至指定的主要函式程式碼的驅動程式分派常式。 假設函式程式碼是 IRP_MJ_READ 或 IRP_MJ_WRITE,則分派常式為 DDDispatchReadWrite。
呼叫 IoGetCurrentIrpStackLocation
任何需要 IRP 參數的驅動程式常式都必須呼叫 IoGetCurrentIrpStackLocation ,以取得驅動程式的 I/O 堆疊位置。 這類常式包括處理多個主要 I/O 函式程式碼的分派常式 (IRP_MJ_*XXX) 、處理支援次要函式 (IRP_MN_XXX) 的函式,或處理 (*IRP_MJ_DEVICE_CONTROL 和/或IRP_MJ_INTERNAL_DEVICE_CONTROL) 的裝置 I/O 控制要求 ,以及處理 IRP 的其他每個驅動程式常式。
此驅動程式的 I/O 堆疊位置是最低位置,且顯示陰影的較高層級驅動程式 I/O 堆疊位置數目不限。 為了簡單起見,上圖不會顯示從DispatchReadWrite、StartIo、AdapterControl和DpcForIsr常式呼叫IoGetCurrentIrpStackLocation。
呼叫 IoMarkIrpPending 和 IoStartPacket
範例驅動程式不會在其分派常式中完成 IRP,而是會改為在其 StartIo 常式中處理 IRP。 在可以這麼做之前,分派常式會呼叫 IoMarkIrpPending 來表示 IRP 尚未完成。 然後它會呼叫 IoStartPacket ,將 IRP 排入佇列,以供驅動程式的 StartIo 常式進一步處理。 分派常式也會傳回 NTSTATUS 值STATUS_PENDING。
下圖說明 IoStartPacket的呼叫。
如果驅動程式忙於處理裝置上的另一個 IRP, IoStartPacket 會將 IRP 插入與裝置物件相關聯的裝置佇列中。 驅動程式可以選擇性地提供 Key 值做為 IoStartPacket 的參數,以在裝置佇列中的 IRP 上強制執行驅動程式決定的順序。
如果驅動程式不忙碌,且裝置佇列是空的,I/O 管理員會立即呼叫其 StartIo 常式,並傳遞輸入 IRP。
對於大量儲存裝置,最低層級驅動程式不需要在呼叫IoStartPacket時提供Cancel常式,原因有兩個:
分層在這類驅動程式上的檔案系統通常會處理檔案 I/O 要求的取消。
大量儲存設備磁碟機會快速處理 IRP。
通常,多層式驅動程式鏈結中的最高層級驅動程式會處理 IRP 的取消。
呼叫 AllocateAdapterChannel 和 MapTransfer
假設StartIo常式是透過最低層級驅動程式常式說明 IRP 路徑的圖中所示,會決定傳輸要求可由單一 DMA 作業完成,StartIo常式會使用驅動程式AdapterControl常式的進入點和 IRP 呼叫AllocateAdapterChannel。
當系統 DMA 控制器可供使用時,I/O 管理員會呼叫驅動程式的 AdapterControl 常式來設定傳輸作業。 AdapterControl常式會呼叫MapTransfer來設定系統 DMA 控制器。 然後,驅動程式會為其裝置進行 DMA 作業並傳回。 (如需使用 DMA 和配接器物件的詳細資訊,請參閱 輸入/輸出技術.)
從驅動程式的 ISR 呼叫 IoRequestDpc
當裝置中斷以指出其傳輸作業完成時,驅動程式的 ISR 會停止裝置產生中斷並呼叫 IoRequestDpc,如下圖所示,說明透過最低層級驅動程式常式的 IRP 路徑。
此呼叫會將驅動程式的 DpcForIsr 常式排入佇列,以盡可能以較低的硬體優先順序完成傳輸作業, (IRQL) 。
呼叫 IoStartNextPacket 和 IoCompleteRequest
當 DpcForIsr 常式完成傳輸處理時,它會立即呼叫 IoStartNextPacket ,以便在裝置佇列中有下一個 IRP 時呼叫驅動程式的 StartIo 常式。 DpcForIsr常式也會設定剛完成的 IRP I/O 狀態欄塊,然後呼叫IRP 的 IoCompleteRequest。
下圖說明此驅動程式對 IoStartNextPacket 和 IoCompleteRequest的呼叫。
驅動程式應該呼叫 IoStartNextPacket 或 IoStartNextPacketByKey ,儘快開始下一個要求的 I/O 作業,最好在呼叫 IoCompleteRequest之前先開始。
如果裝置有任何 IRP 已排入佇列, IoStartNextPacket 會呼叫 KeRemoveDeviceQueue ,以從佇列中移除下一個 IRP。 接著,I/O 管理員會呼叫驅動程式的 StartIo 常式,並傳遞已清除佇列的 IRP。 如果裝置佇列中目前沒有 IRP, IoStartNextPacket 只會返回呼叫端。
在 IRP 中設定 I/O 狀態欄塊
呼叫IoCompleteRequest之前,每個最低層級驅動程式都必須設定 IRP 的I/O 狀態欄塊。 (在上圖中,第二個陰影區域代表狀態欄塊。) I/O 狀態欄塊會將資訊提供給較高層級的驅動程式,最後也會提供給 I/O 作業的原始要求者。 上圖中驅動程式上階層式任何較高層級驅動程式可能都已設定 IoCompletion 常式,以讀取此驅動程式所設定的 I/O 狀態欄塊。 較高層級的驅動程式通常不會修改設備磁碟機已完成之 IRP 中的 I/O 狀態欄塊,除非較高層級的驅動程式正在重試 IRP,在此情況下,它會重新初始化 I/O 狀態欄塊。
在呼叫 IoCompleteRequest之前,必須先在該 IRP 中設定 IRP 中的 I/O 狀態欄塊,才能完成 IRP,而不將其傳送至下一個較低驅動程式。 為了獲得良好的整體 I/O 輸送量,較高層級驅動程式應該檢查每個 IRP 本身 I/O 堆疊位置中的參數,如果參數無效,應該設定 I/O 狀態欄塊並完成要求本身。 可能的話,驅動程式應該避免將不正確要求傳遞至鏈結中的較低驅動程式。
假設上圖中的傳輸作業成功, DpcForIsr 常式會顯示在說明 IRP 路徑透過最低層級驅動程式常式的 IRP 路徑、在 Status 中設定STATUS_SUCCESS,以及 IRP I/O 狀態欄塊 的資訊 中傳輸的位元組數目。
許多標準驅動程式常式也會傳回 NTSTATUS 類型值。 如需 NTSTATUS 常數的詳細資訊,例如 STATUS_SUCCESS,請參閱 記錄錯誤。