延遲過程調用(DPC)是Microsoft Windows作業系統的機制,允許高優先權任務如中斷處理程式延遲所需的低優先權任務稍後執行。這使得設備驅動程式與其他低層事件消費者更快地執行其處理的高優先權部分,調度非關鍵的附屬檔案處理稍後以較低優先權執行。
基本介紹
- 中文名:延遲過程調用
- 外文名:Delayed procedure call
- 縮寫:DPC
- 作用:是微軟視窗作業系統機制
簡介,技術細節,設備驅動編程,
簡介
DPC是通過DPC對象實現的。當設備驅動程式或其他核心態程式發出DPC請求時,作業系統核心創建DPC對象,投寄到DPC佇列尾部。多核處理器的每個核心有自己的DPC佇列。當Windows作業系統的IRQL降低到Dispatch/DPC級,作業系統檢查DPC佇列,逐個執行掛起的DPC,直至佇列為空或者發生IRQL更高的中斷。
例如,當時鐘中斷髮生時,時鐘中斷服務程式通常增加當前執行緒的計數器以計算當前執行緒的總執行時間,把它的時間片減1。當時間片減少到0,執行緒調度器將被喚醒去選擇下一個在當前處理器核心上運行的執行緒並做執行緒上下文切換。時鐘中斷服務程式運行在非常高的IRQL上,它應該在稍後的IRQL下降到低級時才去執行執行緒調度程式。因此時鐘中斷服務程式請求一個DPC對象並把這個對象放在DPC佇列尾部,當處理器的IRQL下降到DPC/Dispatch級別時,回去執行DPC佇列中的任務。
當流音視頻工作時,使用DPC處理流輸入到緩衝區的音頻。如果另外一個DPC執行了很長時間並且另一次中斷產生了新的緩衝區數據,在第一塊數據被處理前,會發生信號缺失結果。
技術細節
具體說,CPU的每個核心對應的_KPRCB數據結構中,有兩個DPC佇列:
- (普通)DPC佇列:普通的DPC可以在任何一個執行緒環境中運行
- 執行緒化DPC佇列:這種DPC只能在核心啟動時系統創建的一個叫做DPC的執行緒中運行。該執行緒工作在passive層,但有最高的執行緒優先權(Realtime級)。因此,CPU一旦工作在passive級,最先執行的就是執行緒化DPC例程中的代碼。
DPC執行例程一般較大,為避免當前執行緒的運行棧溢出,DPC例程使用每個CPU專門的DPC運行棧。
DPC有三種優先權:
- low:入隊時放在佇列頭部,僅當佇列長度超限或者DPC請求率太低,才會被執行。
- medium:默認值。入隊時放在佇列尾部,總是被投遞(即執行)。
- high:入隊時放在佇列頭部,總是被投遞(即執行)。
DPC對象是Windows核心對象之一,其數據結構定義為:
Typedef struct _KDPC{ UCHAR Type;//DpcObject或者ThreadedDpcObject類型 UCHAR Importance;//High,medium,low USHORT Number;//CPU的哪個核上 LIST_ENTRY DpcListEntry;//鍊表結構 PKDEFERRED_ROUTINE DeferredRoutine;//DPC例程 PVOID DeferredContext;//執行DPC時的上下文 PVOID SystemArgument1;//執行DPC時的參數1 PVOID SystemArgument2;//執行DPC時的參數2 Volatile PVOID DpcData;//指向CPU的核的數據結構_KPRCB中的DpcData成員}KDPC
DPC對象入隊時,核心會請求一個在Dispatch/DPC級的軟體中斷。當IRQL降低到APC或者PASSIVE級時,會投遞一個APC對象並把IRQL升到DPC級。此時DPC例程會在任何任意進程的運行環境中,因此DPC例程通常只使用核心地址空間。
設備驅動編程
設備驅動編程中使用下述API來管理延遲過程調用:
- IolnitimizeDpcRequest:初始化設備對象的內置DPC對象,同時註冊一個DPC例程。由KeInitializeDpc將DPC對象與DPC例程、驅動程式創建的設備對象聯繫起來。
- IoRequestDpc:請求一個DPC調用,由KeInsertQueueDpc函式將DPC對象排隊。如果在DPC例程運行前,該設備又生成一個中斷,系統核心將忽略隨後入隊的任何DPC對象。即每個設備只能有一個DPC對象。
- KeSetTargetProcessorDpc:把一個DPC對象送入特定處理器的DPC佇列
- KeSetImportanceDpc:設定DPC對象的優先權
- KiRetireDpcList:逐個執行DPC佇列中的所有對象