IO管理器的任務就是管理IO,本質上windows的IO操作都是異步的,這是由IO流的分層下遞和IRQL共同決定的,效果就是IO流被處 理的每一個步驟都可能在任意線程上下文中進行,那麼如果最下面的驅動完成了一個irp,這個怎麼讓上面的驅動知道呢?當然方法很多,比如上層驅動等待在一 個event上,而microsoft的作法顯然更好,就是為每一個irp流經的每一個裝置提供了一個選擇,該裝置的驅動可以提供一個回調函數,以便下層 驅動完成請求的時候通知該層,這就是IO完成回調函數,按照正常了解,這個函數應該由下層的驅動調用,而實際上這樣做的話,實作就不是那麼“結構化”了, 驅動的開發者就必須負責去手工調用該函數,失去了回調本來的含義,增加了驅動開發者的負擔,是以這個回調函數就由IO管理器來調用,這裡的說法僅僅是按照 子產品化思想來的,在整個windows核心中你實際上看不到一個叫做IO管理器的子產品的,所謂的IO管理器就是一系列函數接口,對于IO完成回調函數的調 用,你隻需要調用一個IofCompleteRequest就可以了,而這個接口的實作就是IO管理器的重要内容,那麼它是怎麼實作的呢?如果不看 ReactOS的代碼,也不反彙編windows核心的話,還有一種情況能夠得到它的實作,那就是自己想,我就是這麼幹的,沒想到還真蒙對了。
在分層的驅動逐次往下層的裝置遞交irp的過程中,隻要有一個分發函數調用了IofCompleteRequest,那麼該irp就不會再往下走了,而是 順着來的方向的反方向逐個的調用irp棧上的IO完成回調函數,是以IofCompleteRequest的實作首先就是周遊所有的irp棧幀,然後在每 一個上執行其IO完成回調函數即可,這隻是最簡單的方式,windows核心的邏輯也是這麼來的,但是往往自己能想到的都是最簡單的,擴充開來的話會有很 多自己想不到的,比如有一種情況就是irp棧某一層完成了io,IO管理器調用了上層的io完成回調,接下來該回調傳回以後,IO管理器會接着調用上上層 的io完成回調,然而如果這個上層的io完成回調中如果一個資料沒有準備好,而上上層io完成回調中又需要這個資料的話,這就會引起問題,IO管理器直接 調用了上上層的io完成回調,而實際上這個上層的io還沒有完成呢(一個資料沒有到位)。以上這種情況是很常見的,畢竟windows核心是一個本質上異 步的作業系統核心,一個irp到達某個層次後可能為了實作某項功能會進行一些别的操作,比如查詢一些資訊,比如和别的核心子產品進行互動,這都會産生另外的 irp,而這些irp是和上層沒有關系的,是在本層産生并下發的,是以如果這些irp不完成,那個本來的irp也就算沒有完成,是以雖然下層完成了這個本 來的irp,隻因為這些額外的我們自己在本層生成的irp還沒有完成,這就意味着IO完成不能這麼簡單的逆流而上,實際上最簡單的方式就是告訴IO管理 器,也就是告訴IofCompleteRequest不要繼續逆流而上了,等到時機成熟的時候,我自己逆流而上,這就是IO完成回調函數 STATUS_MORE_PROCESSING_REQUIRED傳回值的含義,IofCompleteRequest一旦接收到某個回調函數的傳回值是 這個,那麼就終止循環,然後退出IO管理器,等到這個層次的所有資料都準備好之後,該驅動自行調用IofCompleteRequest,這将重複同樣的 過程,隻是起點變化了,不是最底層的驅動了,而是該層驅動,也就是因為資料而等待的驅動,資料可以在io完成中被準備好,也可以是别的,這就是另外的話題 了。
最終irp會回到它生成的地方,到這裡之後,大多數情況下仍然是任意線程上下文,因為最底層驅動的完成是靠中斷觸發,是以一系列的逆流而上就是在DPC中 執行的(DPC在cpu的隊列中排隊,為了延遲執行硬中斷任務,而APC線上程隊列中排隊,執行線程上下文中執行的任務),是以為了将完成消息通知到發出 請求的線程,就需要将最後的irp層次的io完成回調函數排入到該線程的apc隊列中,然後最終在該線程的上下文中被執行,實際上排入apc隊列的是 IopCompleteRequest函數。底層驅動不能立馬完成的irp,傳回時一定要設定pending狀态,這樣上層才可以正确處理完成回調函數, 注意,這個pending和STATUS_MORE_PROCESSING_REQUIRED的含義是不同 的,STATUS_MORE_PROCESSING_REQUIRED是自己來處理接下來的完成回調,而pending則是訓示io還沒有完成,可是完成 回調卻要調用,為了防止阻塞,真正的完成操作将在dpc/apc中進行。
本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1273452