管理裝置佇列

除了 FSD 以外,I/O 管理員通常會 (,) 驅動程式呼叫 IoCreateDevice時建立相關聯的裝置佇列物件。 它也提供 IoStartPacketIoStartNextPacket,驅動程式可以呼叫該套件,讓 I/O 管理員將 IRP 插入相關聯的裝置佇列或呼叫其 StartIo 常式。

因此,驅動程式很少需要 (或特別有用的) ,才能為 IRP 設定自己的裝置佇列物件。 可能候選項目是驅動程式,例如 SCSI 埠驅動程式,必須協調來自透過單一控制器或匯流排介面卡服務之異質裝置之一些緊密結合類別驅動程式的連入 IRP。

換句話說,磁片陣列控制器的驅動程式較可能使用驅動程式建立的控制器物件,而不是 (設定補充裝置佇列物件 () ,而附加元件匯流排介面卡的驅動程式和一組類別驅動程式較可能使用補充裝置佇列。

搭配 StartIo 常式使用補充裝置佇列

藉由呼叫IoStartPacket 和 IoStartNextPacket,驅動程式的 Dispatch 和DpcForIsr (或CustomDpc) 常式會使用驅動程式建立裝置物件時所建立的裝置佇列,將呼叫同步至其StartIo常式。 針對具有 StartIo 常式的埠驅動程式, IoStartPacketIoStartNextPacket 會在埠驅動程式的共用裝置控制器/配接器裝置佇列中插入和移除 IRP。 如果埠驅動程式也設定補充裝置佇列,以保存來自緊密結合較高層級類別驅動程式的要求,它必須「排序」傳入 IRP 到其補充裝置佇列,通常是在其 StartIo 常式中。

埠驅動程式必須在嘗試將該 IRP 插入適當的佇列之前,判斷每個 IRP 所屬的補充裝置佇列。 目標裝置物件的指標會隨著 IRP 傳遞至驅動程式的 Dispatch 常式。 驅動程式應該儲存指標,以用於傳入 IRP 的「排序」。 請注意,傳遞至 StartIo 常式的裝置物件指標是驅動程式自己的裝置物件,其代表裝置控制器/介面卡,因此無法用於此用途。

將任何 IRP 排入佇列之後,驅動程式會執行其共用控制器/配接器以執行要求。 因此,埠驅動程式可以處理所有裝置的傳入要求,直到 呼叫 KeInsertDeviceQueue 將 IRP 放入特定類別驅動程式的裝置佇列為止。

藉由使用自己的裝置佇列來處理透過 其 StartIo 常式處理的所有 IRP,基礎埠驅動程式會透過共用裝置 (或匯流排) 控制器/配接器,將作業序列化至所有連接的裝置。 藉由在個別裝置佇列中保留每個支援的裝置的 IRP,此埠驅動程式會禁止處理已忙碌裝置的 I/O,同時針對透過其共用硬體執行 I/O 的每個裝置增加 I/O 輸送量。

為了回應從埠驅動程式的 Dispatch 常式呼叫 IoStartPacket ,I/O 管理員會立即呼叫該驅動程式的 StartIo 常式,或將 IRP 放入與埠驅動程式共用控制器/配接器之裝置物件相關聯的裝置佇列中。

埠驅動程式必須維護其透過共用裝置控制器/配接器服務之每個異質裝置本身的狀態資訊。

使用補充裝置佇列設計類別/埠驅動程式時,請記住下列事項:

  • 除了裝置堆疊頂端的裝置物件之外,驅動程式無法輕易地取得由上方階層式任何驅動程式所建立之裝置物件的指標。

    根據設計,I/O 管理員不提供取得這類指標的支援常式。 此外,載入驅動程式的順序讓較低驅動程式無法取得較高層級驅動程式裝置物件的指標,這些物件在新增其裝置時尚未建立。

    雖然 IoGetAttachedDeviceReference 會傳回驅動程式堆疊中最高層級裝置物件的指標,但驅動程式應該只使用此指標來指定其堆疊之 I/O 要求的目標。 驅動程式不應該嘗試讀取或寫入裝置物件。

  • 除了將要求傳送至它自己的裝置堆疊頂端以外,驅動程式無法使用任何分層在本身上方的驅動程式所建立之裝置物件的指標。

    無法以多處理器安全的方式同步處理單一裝置物件的存取 (及其裝置延伸模組) 。 驅動程式都無法對另一個驅動程式目前正在處理的 I/O 做出任何假設。

即使是緊密結合的類別/埠驅動程式,每個類別驅動程式都應該使用埠驅動程式裝置物件的指標, () 只使用 IoCallDriver來傳遞 IRP。 基礎埠驅動程式必須維護自己的狀態,可能是在埠驅動程式的裝置擴充功能中,其會針對任何緊密結合類別驅動程式處理的要求, () 裝置 () 。

跨驅動程式常式管理補充裝置佇列

針對一組緊密結合的類別驅動程式,將補充裝置佇列中的 IRP 排入佇列的任何埠驅動程式也必須有效率地處理下列情況:

  1. 其分派常式已在該裝置的驅動程式建立裝置佇列中插入特定裝置的 IRP。

  2. 其他裝置的 IRP 會繼續出現,以使用IoStartPacket將驅動程式的StartIo常式排入佇列,並透過共用裝置控制器進行處理。

  3. 裝置控制器不會變成閒置狀態,但驅動程式建立裝置佇列中保留的每個 IRP 也必須儘快排入驅動程式的 StartIo 常式。

因此,每當埠驅動程式完成 IRP 時,埠驅動程式的 DpcForIsr 常式必須嘗試將特定裝置的內部裝置佇列中的 IRP 傳送到共用配接器/控制器的裝置佇列,如下所示:

  1. DpcForIsr常式會呼叫IoStartNextPacket,讓StartIo常式開始處理排入共用裝置控制器的下一個 IRP。

  2. DpcForIsr常式會呼叫KeRemoveDeviceQueue來清除下一個 IRP (如果有任何) 代表其代表其完成 IRP 之裝置的內部裝置佇列中保留的 IRP。

  3. 如果 KeRemoveDeviceQueue 傳回非 Null 指標, DpcForIsr 常式會使用剛清除佇列的 IRP 呼叫 IoStartPacket ,使其排入共用裝置控制器/配接器。 否則, 呼叫 KeRemoveDeviceQueue 只會將裝置佇列物件的狀態重設為 Not-Busy,而 DpcForIsr 常式省略 IoStartPacket的呼叫。

  4. 然後, DpcForIsr 常式會使用埠驅動程式剛完成 I/O 處理的輸入 IRP 呼叫 IoCompleteRequest ,方法是設定 I/O 狀態欄塊並出現錯誤,或滿足 I/O 要求。

請注意,上述序清單示 DpcForIsr 常式也必須判斷其正在完成目前 (輸入) IRP 的裝置,以便有效率地管理 IRP 的內部佇列。

如果埠驅動程式嘗試等到其共用控制器/介面卡閒置,再將其補充裝置佇列中保留的 IRP 清除佇列之前,驅動程式可能會耗盡有大量 I/O 需求的裝置,同時其會立即為目前 I/O 需求較輕的其他每部裝置提供服務。