共用方式為


Lowest-Level驅動程式中的 StartIo 常式

I/O 管理員對驅動程式分派常式的呼叫是滿足裝置 I/O 要求的第一個階段。 StartIo常式是第二個階段。 每個具有StartIo常式的裝置驅動程式都可能會從其DispatchRead 和 DispatchWrite常式呼叫IoStartPacket,而且通常會針對其DispatchDeviceControl常式中支援的 I/O 控制項代碼子集呼叫。 IoStartPacket常式會將 IRP 新增至裝置的系統提供的裝置佇列,或者,如果佇列是空的,請立即呼叫驅動程式的StartIo常式來處理 IRP。

您可以假設在呼叫驅動程式的 StartIo 常式時,目標裝置不會忙碌。 這是因為 I/O 管理員會在兩種情況下呼叫 StartIo ;其中一個驅動程式的分派常式剛呼叫 IoStartPacket 且裝置佇列是空的,或驅動程式的 DpcForIsr 常式正在完成另一個要求,而且剛呼叫 IoStartNextPacket 來取消佇列下一個 IRP。

在呼叫最高層級設備磁碟機的 StartIo 常式之前,該驅動程式的分派常式應該在必要時探查並鎖定使用者緩衝區,以在排入 佇列至 StartIo 常式的 IRP 中設定有效的對應緩衝區位址。 如果最高層級的裝置驅動程式針對直接 I/O (設定其裝置物件,或針對未緩衝處理或直接 I/O) 設定其裝置物件,則驅動程式無法延遲鎖定使用者緩衝區至其 StartIo 常式;在 IRQL = DISPATCH_LEVEL的任意執行緒內容中,會呼叫每個 StartIo 常式。

注意

驅動程式 的 StartIo 常式所要存取的任何緩衝區記憶體都必須鎖定或配置自駐留的系統空間記憶體,而且必須在任意執行緒內容中存取。

一般而言,任何較低層級的裝置驅動程式 StartIo 常式都會負責呼叫具有輸入 IRP 的 IoGetCurrentIrpStackLocation ,然後執行任何要求特定的處理,以在其裝置上啟動 I/O 作業。 要求特定的處理可以包含下列專案:

  • 設定或更新驅動程式維護之目前要求的任何狀態資訊。 狀態資訊可能會儲存在目標裝置物件的裝置延伸模組中,或是驅動程式所配置非分頁集區的其他位置。

    例如,如果設備磁碟機維護目前傳輸作業的 InterruptExpected Boolean,其 StartIo 常式可能會將此變數設定為 TRUE。 如果驅動程式維護目前作業的逾時計數器,其 StartIo 常式可能會設定此值,或者 StartIo 常式可能會將驅動程式的 CustomTimerDpc 常式排入佇列。

    如果 StartIo 常式與其他驅動程式常式共用狀態資訊或 硬體資源的 存取權,狀態資訊或資源必須受到微調鎖定的保護。 (請參閱 微調鎖定.)

    如果 StartIo 常式與驅動程式 的 InterruptService 常式共用狀態資訊或資源的存取權, StartIo 必須使用 KeSynchronizeExecution 來呼叫存取狀態或資源資訊的 SynchCritSection 常式。 (請參閱 使用重要區段。)

  • 如果驅動程式在處理 IRP 時必須記錄裝置 I/O 錯誤,請將序號指派給 IRP。

    如需詳細資訊,請參閱 記錄錯誤

  • 如有必要,將驅動程式 I/O 堆疊位置中的參數轉譯為裝置特定值。

    例如,磁片驅動程式可能需要計算傳輸作業之實體磁片位址的起始磁區或位元組位移,以及傳輸要求的長度是否會跨越特定磁區界限,或超過其實體裝置的傳輸容量。

  • 如果驅動程式控制抽取式媒體裝置,請先檢查媒體變更,再針對 I/O 設計裝置,並在媒體變更時通知其過度處理檔案系統。

    如需詳細資訊,請參閱 支援卸載式媒體

  • 如果裝置使用 DMA,請檢查要求的 Length (要傳輸的位元組數目,在 IRP) 的驅動程式 I/O 堆疊位置中,應該分割成部分傳輸作業,如 輸入/輸出技術中所述,假設緊密結合的較高層級驅動程式不會預先準備裝置驅動程式的大型傳輸。

    這類設備磁碟機的StartIo常式也可以負責呼叫KeFlushIoBuffers,如果驅動程式使用封包型 DMA,則使用驅動程式的AdapterControl常式呼叫AllocateAdapterChannel

    如需詳細資訊,請參閱 配接器物件和 DMA維護快取共合

  • 如果裝置使用 PIO,請將 IRP at Irp-MdlAddress >的緩衝區基底虛擬位址對應至具有 MmGetSystemAddressForMdlSafe的系統空間位址。

    針對讀取要求,設備磁碟機的 StartIo 常式可以負責在 PIO 作業開始之前呼叫 KeFlushIoBuffers 。 如需詳細資訊,請參閱 維護快取共置

  • 如果非 WDM 驅動程式使用控制器物件,請呼叫 IoAllocateController 來註冊其 ControllerControl 常式。

  • 如果驅動程式處理可取消的 IRP,請檢查輸入 IRP 是否已取消。

  • 如果輸入 IRP 可以在處理完成之前取消,StartIo常式必須使用 IRP 和驅動程式Cancel常式的進入點呼叫IoSetCancelRoutineStartIo常式必須取得取消微調鎖定,才能呼叫IoSetCancelRoutine。 或者,驅動程式可以使用IoSetStartIoAttributesStartIo常式的NonCancelable屬性設定為TRUE。 這可防止系統嘗試取消已透過呼叫IoStartPacket傳遞至StartIo的 IRP。

一般規則是,使用緩衝 I/O 的驅動程式具有比使用直接 I/O 的驅動程式更簡單的 StartIo 常式。 針對每個傳輸要求使用緩衝 I/O 傳輸少量資料的驅動程式,而使用直接 I/O 的驅動程式 (DMA 或 PIO 是否) 將大量資料傳送至或移出可跨越系統記憶體中實體頁面界限的鎖定緩衝區。

高於實體設備磁碟機的較高層級驅動程式通常會設定其裝置物件,以符合其各自的設備磁碟機。 不過,最高層級的驅動程式,特別是檔案系統驅動程式,可以針對直接或緩衝的 I/O 設定裝置物件。

針對緩衝 I/O 設定其裝置物件的驅動程式可以依賴 I/O 管理員,在傳送至驅動程式的所有 IRP 中傳遞有效的緩衝區。 針對直接 I/O 設定裝置物件的較低層級驅動程式,可以依賴其鏈結中的最高層級驅動程式,將透過任何中繼驅動程式傳送至基礎較低層級設備磁碟機的所有 IRP 中傳遞有效的緩衝區。

在 StartIo 常式中使用緩衝 I/O

如果驅動程式的 DispatchReadDispatchWriteDispatchDeviceControl 常式判斷要求有效,並呼叫 IoStartPacket,I/O 管理員會呼叫驅動程式的 StartIo 常式,以在裝置佇列是空的時立即處理 IRP。 如果佇列不是空的, IoStartPacket 會將 IRP 排入佇列。 最後,從驅動程式的DpcForIsrCustomDpc常式呼叫IoStartNextPacket會導致 I/O 管理員取消佇列 IRP 並呼叫驅動程式的StartIo常式。

StartIo常式會呼叫IoGetCurrentIrpStackLocation,並判斷必須執行哪些作業以滿足要求。 它會在設計實體裝置以執行 I/O 要求之前,以任何必要的方式預先處理 IRP。

如果存取實體裝置 (或裝置擴充功能) 必須與 InterruptService 常式同步, StartIo 常式必須呼叫 SynchCritSection 常式來執行必要的裝置程式設計。 如需詳細資訊,請參閱 使用重要章節

使用緩衝 I/O 的實體裝置驅動程式,會從 I/O 管理員配置的系統空間緩衝區之間傳輸資料,驅動程式會在 Irp-AssociatedIrp.SystemBuffer > 的每個 IRP中找到。

在 StartIo 常式中使用直接 I/O

如果驅動程式的 DispatchReadDispatchWriteDispatchDeviceControl 常式判斷要求有效,並呼叫 IoStartPacket,I/O 管理員會呼叫驅動程式的 StartIo 常式,以在裝置佇列是空的時立即處理 IRP。 如果佇列不是空的, IoStartPacket 會將 IRP 排入佇列。 最後,從驅動程式的DpcForIsrCustomDpc常式呼叫IoStartNextPacket會導致 I/O 管理員取消佇列 IRP 並呼叫驅動程式的StartIo常式。

StartIo常式會呼叫IoGetCurrentIrpStackLocation,並判斷必須執行哪些作業以滿足要求。 它會以任何必要方式預先處理 IRP,例如將大型 DMA 傳輸要求分割成部分傳輸範圍,以及儲存必須分割之傳入傳輸要求的 長度 狀態。 然後,它會程式化實體裝置來執行 I/O 要求。

如果存取實體裝置 (或裝置擴充功能) 必須與驅動程式的 ISR 同步處理, StartIo 常式必須使用驅動程式提供的 SynchCritSection 常式來執行必要的程式設計。 如需詳細資訊,請參閱 使用重要章節

任何使用直接 I/O 的驅動程式都會將資料讀取或寫入鎖定緩衝區,如記憶體描述元清單 (MDL) 所述,驅動程式會在 Irp-MdlAddress > 的 IRP中找到。 這類驅動程式通常會針對裝置控制要求使用緩衝 I/O。 如需詳細資訊,請參閱 處理 StartIo 常式中的 I/O 控制要求

MDL 類型是驅動程式無法直接存取的不透明類型。 相反地,使用 PIO 的驅動程式會呼叫 MmGetSystemAddressForMdlSafe 搭配 Irp-MdlAddress > 做為參數,以重新對應使用者空間緩衝區。 使用 DMA 的驅動程式也會傳遞 Irp-MdlAddress > ,以支援其傳輸作業期間的常式,讓緩衝區位址重新對應到其裝置的邏輯範圍。

除非緊密結合的較高層級驅動程式會分割基礎設備磁碟機的大型 DMA 傳輸要求,否則最低層級設備磁碟機的 StartIo 常式必須分割大於其裝置可以在單一傳輸作業中管理的每個傳輸要求。 使用系統 DMA 的驅動程式必須分割對系統 DMA 控制器太大的傳輸要求,或讓其裝置在單一傳輸作業中處理。

如果裝置是次級 DMA 裝置,其驅動程式必須透過系統 DMA 控制器與驅動程式配置的配接器物件同步傳輸,代表 DMA 通道,以及驅動程式提供的 AdapterControl 常式。 匯流排主機 DMA 裝置的驅動程式也必須使用驅動程式配置的配接器物件來同步處理其傳輸,而且如果使用系統的封包式 DMA 支援,或AdapterListControl常式使用系統的散佈/收集支援,則必須提供AdapterControl常式。

根據驅動程式的設計,它可能會同步處理實體裝置上的傳輸和裝置控制作業與控制器物件,並提供 ControllerControl 常式。

如需詳細資訊 ,請參閱配接器物件和 DMA控制器物件

處理 StartIo 常式中的 I/O 控制要求

一般而言,驅動程式的DispatchDeviceControl 或 DispatchInternalDeviceControl常式只會傳遞裝置 I/O 控制要求的子集,以供驅動程式的 StartIo常式進一步處理。 驅動程式的 StartIo 常式只需要處理需要裝置狀態變更或傳回目前裝置狀態的變動性資訊的有效裝置控制要求。

每個新的驅動程式都必須支援與相同類型裝置之所有其他驅動程式相同的公用 I/O 控制程式碼集。 系統會將 IRP_MJ_DEVICE_CONTROL要求的公用 裝置類型特定 I/O 控制程式碼定義為緩衝要求。

因此,實體設備磁碟機會針對裝置控制要求在 IRP-AssociatedIrp.SystemBuffer > 的 IRP 中找到的系統空間緩衝區,對每個驅動程式進行資料傳輸。 即使是為直接 I/O 設定其裝置物件的驅動程式,也會使用緩衝 I/O 滿足具有公用 I/O 控制碼的裝置控制要求。

每個 I/O 控制程式碼的定義都會決定是否要緩衝傳送該要求的資料。 針對驅動程式特定 IRP_MJ_INTERNAL_DEVICE_CONTROL 要求,任何私下定義的 I/O 控制程式碼,都可以使用緩衝方法、方法直接或方法來定義程式碼。 一般規則是,如果緊密結合的高階驅動程式必須為該要求配置緩衝區,則任何私用定義的 I/O 控制項程式碼都應該使用 方法定義。

設計 I/O 作業的裝置

通常,最低層級設備磁碟機中的 StartIo 常式必須使用 KeSynchronizeExecution 呼叫驅動程式提供的 SynchCritSection 常式,同步存取任何記憶體或裝置與驅動程式的 ISR 共用。 驅動程式的 StartIo 常式會使用 SynchCritSection 常式,在 DIRQL 實際為 I/O 撰寫實體裝置的程式。 如需詳細資訊,請參閱 使用重要章節

呼叫 KeSynchronizeExecution之前, StartIo 常式必須執行要求所需的任何前置處理。 前置處理可能包括計算初始部分傳輸範圍,以及儲存其他驅動程式常式原始要求的任何狀態資訊。

如果裝置驅動程式使用 DMA,其StartIo常式通常會使用驅動程式提供的AdapterControl常式呼叫AllocateAdapterChannel。 在這些情況下, StartIo 常式會將將實體裝置程式設計為 AdapterControl 常式的責任延後。 接著,它可以呼叫 KeSynchronizeExecution ,讓驅動程式提供的 SynchCritSection 常式程式設計裝置以進行 DMA 傳輸。