天天看點

IRP請求處理及完成機制

      近來學習 Windows 核心方面的東西,覺得對 I/O 處理過程沒有一個總體的概念。于是,就花了很長的時間搜集了很多這方面的知識總結了一下。

      在 Windows 核心中的請求基本上是通過 I/O Request Packet 完成的。前面說過,裝置對象是唯一可以接受請求的實體。下面,我就來詳細地說下 IRP 請求是怎麼樣一步一步完成的。

      首先,我們就需要知道 IRP 是怎麼産生。 IRP 是由 I/O 管理器發出的, I/O 管理器是使用者态與核心态之間的橋梁,當使用者态程序發出 I/O 請求時, I/O 管理器就捕獲這些請求,将其轉換為 IRP 請求,發送給驅動程式。 I/O 管理器無疑是非常重要的,具有核心地位。它負責所有 I/O 請求的排程和管理工作,根據請求的不同内容,選擇相應的驅動程式對象,裝置對象,并生成、發送、釋放各種不同的 IRP 。整個 I/O 處理流程是在它的指揮下完成的。

一個 IRP 是從非分頁記憶體中配置設定的可變大小的結構,它包括兩部分: IRP 首部和輔助請求參數數組,如圖 1 所示。這兩部分都是由 I/O 管理器建立的。

圖 1 IRP 簡單結構圖

IRP 首部中包含了指向 IRP 輸入輸出緩沖區指針、目前擁有 IRP 的驅動指針等。

       緊接着首部的是一個 IO_STACK_LOCATION 結構的數組。它的大小由裝置棧中的裝置數确定(裝置棧的概念會在下文中闡述)。 IO_STACK_LOCATION 結構中儲存了一個 I/O 請求的參數及代碼、請求目前對應的裝置指針、完成函數指針( IoCompletion )等。

那麼,由 I/O 管理器産生的 IRP 請求發送到哪去了呢?這裡我們就要來說說裝置棧的概念了,作業系統用裝置對象( device object )表示實體裝置,每一個實體裝置都有一個或多個裝置對象與之相關聯,裝置對象提供了在裝置上的所有操作。也有一些裝置對象并不表示實體裝置。一個唯軟體驅動程式( software-only driver ,處理 I/O 請求,但是不把這些請求傳遞給硬體)也必須建立表示它的操作的裝置對象。裝置常常由多個裝置對象所表示,每一個裝置對象在驅動程式棧( driver stack )中對應一個驅動程式來管理裝置的 I/O 請求。一個裝置的所有裝置對象被組織成一個裝置棧( device stack )。而且, IO_STACK_LOCATION 數組中的每個元素和裝置棧中的每個裝置是一一對應的,一般情況下,隻允許層次結構中的每個裝置對象通路它自己對應的 IO_STACK_LOCATION 。無論何時,一個請求操作都在一個裝置上被完成, I/O 管理器把 IRP 請求傳遞給裝置棧中頂部裝置的驅動程式( IRP 是傳遞給裝置對象的,通過裝置對象的 DriverObject 成員找到驅動程式)。驅動程式通路它對應的裝置對象在 IRP 中 IO_STACK_LOCATION 數組中的元素檢查參數,以決定要進行什麼操作(通過檢查結構中的 MajorFunction 字段,确定執行什麼操作及如何解釋 Parameters 共用體字段的内容)。驅動程式可以根據 IO_STACK_LOCATION 結構中的 MajorFunction 字段進行處理。每一個驅動或者處理 IRP ,或者把它傳遞給裝置棧中下一個裝置對象的驅動程式。

傳遞 IRP 請求到底層裝置的驅動程式需要經過下面幾個步驟:

1.       為下一個 IO_STACK_LOCATION 結構設定參數。可以有以下兩種方式:

·  調用 IoGetNextIrpStackLocation 函數獲得下個結構的指針,再對參數進行指派;

·  調用 IoCopyCurrentIrpStackLocationToNext 函數(如果第 2 步中驅動設定了 IoCompletion 函數 ),或者調用 IoSkipCurrentIrpStackLocation 函數(如果第 2 步中驅動沒有設定 IoCompletion 函數 )把目前的參數傳遞給下一個。

2.       如果需要的話,調用 IoSetCompletionRoutine 函數設定 IoCompletion 函數進行後續處理。

3.       調用 IoCallDriver 函數将 IRP 請求傳遞給下一層驅動。這個函數會自動調整 IRP 棧指針,并且執行下一層驅動的派遣函數。

     當驅動程式把 IRP 請求傳遞給下一層驅動之後,它就不再擁有對該請求的通路權,強行通路會導緻系統崩潰。如果驅動程式在傳遞完之後還想再通路該請求,就必須要設定 IoCompletion 函數。 IRP 請求可以再其他驅動程式或者其他線程中完成或取消。

      當某一驅動程式調用 IoCompleteRequest 函數時, I/O 操作就完成了。這個函數使得 IRP 的堆棧指針向上移動一個位置,如圖 2 所示:

圖 2 IRP 完成時棧指針的移動

      圖 2 所示的當 C 驅動程式調用完 IoCompleteRequest 函數後 I/O 棧的情況。左邊的實線箭頭表明棧指針現在指向驅動 B 的參數和回調函數;虛線箭頭是之前的情況。右邊的空心箭頭指明了 IoCompletion 函數被調用的順序。

如果驅動程式把 IRP 請求傳遞給裝置棧中的下層裝置之前設定了 IoCompletion 函數,當 I/O 棧指針再次指回到該驅動程式時, I/O 管理器就将調用該 IoCompletion 函數。

IoCompletion 函數的傳回值有兩種:

( 1 ) STATUS_CONTINUE_COMPLETION :告訴 I/O 管理器繼續執行上層驅動程式的 IoCompletion 函數。

( 2 ) STATUS_MORE_PROCESSING_REQUIRED :告訴 I/O 管理器停止執行上層驅動程式,并将棧指針停在目前位置。在目前驅   動程式調用 IoCompleteRequest 函數後再繼續執行上層驅動的 IoCompletion 函數。

當所有驅動都完成了它們相應的子請求時, I/O 請求就結束了。 I/O 管理器從 Irp‑>IoStatus.Status 成員更新狀态資訊,從 Irp‑>IoStatus.Information 成員更新傳送位元組數。

寫的比較倉促,如有不正之處,希望大家指教!

繼續閱讀