實作 IoCompletion 常式
在專案上, IoCompletion 常式會收到 CoNtext 指標。 當分派常式呼叫 IoSetCompletionRoutine時,它可以提供 CoNtext 指標。 此指標可以參考 IoCompletion 常式處理 IRP 所需的任何驅動程式判斷內容資訊。 請注意,內容區域無法分頁,因為 IoCompletion 常式可以在 IRQL = DISPATCH_LEVEL呼叫。
請考慮下列 IoCompletion 常式的實作指導方針:
IoCompletion常式可以檢查 IRP 的I/O 狀態欄塊,以判斷 I/O 作業的結果。
如果使用 IoAllocateIrp 或 IoBuildAsynchronousFsdRequest來配置發送常式的輸入 IRP, IoCompletion 常式必須呼叫 IoFreeIrp 來釋放該 IRP,最好在完成原始 IRP 之前。
IoCompletion常式必須釋放針對驅動程式配置的 IRP 所配置之分派常式的任何每個 IRP 資源,最好在釋放對應的 IRP 之前。
例如,如果分派常式使用 IoAllocateMdl 配置 MDL ,並針對它配置的部分傳輸 IRP 呼叫 IoBuildPartialMdl , IoCompletion 常式必須使用 IoFreeMdl 釋放 MDL。 如果配置資源來維護原始 IRP 的狀態,則必須釋放這些資源,最好在呼叫具有原始 IRP 的 IoCompleteRequest 之前,一定在傳回控制權之前。
一般而言,在釋放或完成 IRP 之前, IoCompletion 常式應該釋放分派常式所配置的任何每個 IRP 資源。 否則,驅動程式必須在其 IoCompletion 常式從完成原始要求傳回控制權之前,維護要釋放之資源的狀態。
如果 IoCompletion 常式無法使用 STATUS_SUCCESS 完成原始 IRP,則必須將原始 IRP 中的 I/O 狀態欄塊設定為驅動程式配置 IRP 中傳回的值,導致 IoCompletion 常式失敗原始要求。
如果 IoCompletion 常式使用 STATUS_PENDING 完成原始要求,它必須先使用原始 IRP 呼叫 IoMarkIrpPending ,才能呼叫 IoCompleteRequest。
如果 IoCompletion 常式必須讓原始 IRP 失敗,並出現錯誤STATUS_XXX,它可以 記錄錯誤。 不過,基礎設備磁碟機必須負責記錄任何發生的裝置 I/O 錯誤,因此 IoCompletion 常式通常不會記錄錯誤。
當 IoCompletion 常式已處理並釋放驅動程式配置的 IRP 時,常式必須傳回具有STATUS_MORE_PROCESSING_REQUIRED的控制權。
從 IoCompletion 常式傳回STATUS_MORE_PROCESSING_REQUIRED,會將驅動程式配置和釋放 IRP 的 I/O 管理員完成處理。 第二次呼叫 IoCompleteRequest 會導致 I/O 管理員繼續呼叫 IRP 的完成常式,從傳回STATUS_MORE_PROCESSING_REQUIRED的常式正上方的完成常式開始。
如果 IoCompletion 常式重複使用傳入的 IRP,將一或多個要求傳送至較低的驅動程式,或常式重試失敗的作業,它應該更新 IoCompletion 常式維護每個 IRP 重複使用或重試的內容。 然後,它可以再次設定下一個較低驅動程式的 I/O 堆疊位置、使用自己的進入點呼叫 IoSetCompletionRoutine ,並為 IRP 呼叫 IoCallDriver 。
IoCompletion常式不應該在每次重複使用或重試 IRP 時呼叫IoMarkIrpPending。
分派常式已將原始 IRP 標示為擱置中。 在鏈結中的所有驅動程式都使用 IoCompleteRequest完成原始 IRP 之前,它仍會擱置中。
重試要求之前, IoCompletion 常式應該重設 I/O 狀態欄塊,並針對 [狀態 ] 和 [狀態] 設定為 STATUS_SUCCESS 零,可能是儲存傳回的錯誤資訊之後。
針對每次重試, IoCompletion 常式通常會遞減分派常式所設定的重試計數。 一般而言, IoCompletion 常式必須呼叫 IoCompleteRequest ,當某些有限的重試次數失敗時,IRP 就會失敗。
IoCompletion常式必須在呼叫IoSetCompletionRoutine和IoCallDriver且具有重複使用或重試的 IRP 之後傳回STATUS_MORE_PROCESSING_REQUIRED。
從 IoCompletion 常式傳回STATUS_MORE_PROCESSING_REQUIRED,會讓 I/O 管理員完成重複使用或重試的 IRP 處理。
如果 IoCompletion 常式無法使用 STATUS_SUCCESS完成原始 IRP,它必須讓較低驅動程式傳回 I/O 狀態欄塊,以便重複使用或重試作業,使 IoCompletion 常式失敗 IRP。
如果 IoCompletion 常式使用 STATUS_PENDING 完成原始要求,它必須先使用原始 IRP 呼叫 IoMarkIrpPending ,才能呼叫 IoCompleteRequest。
如果 IoCompletion 常式必須讓原始 IRP 失敗,並出現錯誤STATUS_XXX,它可以 記錄錯誤。 不過,基礎設備磁碟機必須負責記錄任何發生的裝置 I/O 錯誤,因此 IoCompletion 常式通常不會記錄錯誤。
任何在 IRP 中設定IoCompletion常式的驅動程式,然後將 IRP 向下傳遞至較低的驅動程式,都應該檢查IoCompletion常式中的IRP-PendingReturned >旗標。 如果已設定旗標, IoCompletion 常式就必須使用 IRP 呼叫 IoMarkIrpPending 。 不過請注意,將 IRP 向下傳遞,然後等候事件的驅動程式不應該標示 IRP 擱置中。 相反地,其 IoCompletion 常式應該發出訊號,並傳回STATUS_MORE_PROCESSING_REQUIRED。
IoCompletion常式必須釋放配置處理原始 IRP 之分派常式的任何資源,最好在IoCompletion常式呼叫IoCompleteRequest與原始 IRP 之前,且絕對在IoCompletion常式傳回控制項之前,才能完成原始 IRP。
如果任何較高層級的驅動程式已在原始 IRP 中設定其 IoCompletion 常式,則除非呼叫所有較低層級驅動程式的 IoCompletion 常式,否則不會呼叫該驅動程式的 IoCompletion 常式。
在呼叫 IoCompleteRequest 中提供優先權提升
如果最低層級設備磁碟機可以在其分派常式中完成 IRP,它會使用priorityBoost IO_NO_INCREMENT 呼叫IoCompleteRequest。 因為驅動程式可以假設原始要求者未等待其 I/O 作業完成,所以不需要增加執行時間優先順序。
否則,最低層級驅動程式會提供系統定義的裝置類型特定值,以提升要求者的執行時間優先順序,以補償要求者在其裝置 I/O 要求上等候的時間。 如需提升值,請參閱 Wdm.h 或 Ntddk.h。
較高層級的驅動程式在呼叫IoCompleteRequest時,會套用與其各自的基礎設備磁碟機相同的PriorityBoost。
呼叫 IoCompleteRequest 的效果
當驅動程式呼叫 IoCompleteRequest時,I/O 管理員會在呼叫下一個較高層級驅動程式之前,以零填入該驅動程式的 I/O 堆疊位置,如果有的話,該驅動程式已設定要針對 IRP 呼叫 的 IoCompletion 常式。
較高層級驅動程式的 IoCompletion 常式只能檢查 IRP 的 I/O 狀態欄塊,以判斷所有較低驅動程式如何處理要求。
IoCompleteRequest的呼叫端不得嘗試存取剛完成的 IRP。 這類嘗試是導致系統損毀的程式設計錯誤。