使用IoMarkPending的原因及原理

爲了使系統吞吐量最大化,I/O管理器希望驅動程序推遲其耗時IRP的完成。驅動程序通過在某個派遣例程中調用IoMarkIrpPending函數並返回STATUS_PENDING來表示完成操作被推遲。(注意:以下討論未加說明均指在操作被推遲情況下)

I/O管理器的原始調用者通常希望在繼續執行之前等待操作完成,所以I/O管理器在處理推遲完成時有下面類似的邏輯(不代表真正的Microsoft源代碼):

 

Irp->UserEvent = pEvent;            //  don't do this yourself

status = IoCallDriver(...);

if (status == STATUS_PENDING)

  KeWaitForSingleObject(pEvent, ...);

以上內容可總結爲:頂級驅動程序安裝了一個完成例程(附註:也可不安裝完成例程)並調用IoCallDriver。這個過程被重複幾次,經過中間的驅動程序(中間驅動程序也可以選擇安裝完成例程),當IRP到達能處理它的那個驅動程序級時,派遣例程就調用IoMarkIrpPending並返回STATUS_PENDING。然後該STATUS_PENDING值按原路返回到頂級驅動程序,最後回到IRP的發起者。IRP發起者檢查從IoCallDriver得到的任何值,若檢查到STATUS_PENDING從而發起者將立即在那個事件上等待,直到某個代碼使那個事件變爲通知狀態。

系統是通過讓IoCompleteRequest調度APC來使那個事件變成通知狀態的,對於某些IRP,IoCompleteRequest會毫無條件地調度APC,而對於某些IRP,IoCompleteRequest會檢查堆棧頂部IO_STACK_LOCATION中是否設置了SL_PENDING_RETURNED標誌,只有這個標誌被設置時才調度APC。

 

調度APC的作用:該APC會做很多清理工作,比如調用IoFreeIrp釋放IRP、調用KeSetEvent使IRP發起者等待的事件爲通知狀態等等。

 

“被推遲的操作”結束後的處理過程:某個能處理IRP的派遣例程返回STATUS_PENDING前會首先調用IoMarkIrpPending函數在當前的堆棧單元中設置一個名爲SL_PENDING_RETURNED的標誌;當被推遲的操作在將來某個時候結束時,該派遣例程中的IoCompleteRequest函數會檢查到SL_PENDING_RETURNED被設置,同時它會檢查上層驅動是否安裝了完成例程,若未設置,則它會把當前堆棧單元的SL_PENDING_RETURNED標誌複製到上一層堆棧單元中,若設置了,則在調用完成例程前它不會把當前堆棧單元的SL_PENDING_RETURNED標誌複製到上一層堆棧單元中,而僅僅在IRP中設置PendingReturned標誌爲SL_PENDING_RETURNED標誌(Windows NT就是這麼設計的…),因此上層驅動的完成例程就有義務在自己的堆棧單元中設置SL_PENDING_RETURNED標誌。這就是爲什麼我們經常在完成例程中看到這2行代碼:         if (Irp->PendingReturned)                  IoMarkIrpPending(Irp);上面的過程是從堆棧下層開始逐步向上層執行的,隨着SL_PENDING_RETURNED逐步向上層堆棧傳遞,最終最頂層堆棧單元中的SL_PENDING_RETURNED標誌會被設置,從而可以保證IoCompleteRequest函數會調度APC來使IRP發起者等待的事件變爲通知狀態。

 

總結:調用IoMarkPending的作用是設置當前堆棧的SL_PENDING_RETURNED標誌,最終目的是使SL_PENDING_RETURNED標誌可以從下層堆棧單元逐步傳遞到堆棧頂部單元,這樣就可以保證IoCompleteRequest函數會調度APC使IRP發起者免於永久阻塞。


本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/gxfan/archive/2008/07/28/2722874.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章