近來學習 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 成員更新傳送字節數。
寫的比較倉促,如有不正之處,希望大家指教!
本文來自CSDN博客,轉載請標明出處:
http://blog.csdn.net/vangoals/archive/2009/07/20/4363863.aspx
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
