APC 基本概念及APC注入的實現(Ring3 + Ring0)----概念介紹
APC 基本概念及APC注入的實現(Ring3 + Ring0)—-概念介紹
基本術語
中斷—-一個非同步事件,可能在任何時候發生,與處理器當前正在執行的程式碼無任何關係。—-中斷的產生主要有:I/O 裝置、處理器始終,或者定時器,另外,終端可以被開啟或者被禁止。
異常是一個同步事件,它是一個特殊指令執行的結果,特點就是可重複與可預見性。在同樣的條件下用同樣的資料第二次執行一個程式可以重現原來的異常。比如:記憶體訪問違例、特定的偵錯程式指令,除零錯誤等。
系統陷阱—即系統服務呼叫,核心把它也看作一種異常。
- 陷阱(trap)—-“當異常或者中斷髮生時,處理器捕捉到一個執行執行緒,並且將控制權轉移到作業系統中某一固定地址處”的一種機制。
- 陷阱處理器—-指的是與某個特殊的中斷或異常相關聯的一個函式。在Windows 中,陷阱概念中的固定地址就是陷阱處理器。
- 通用的系統服務陷阱處理器—-在核心將控制權轉交給與該陷阱相關的其它函式之前或之後,由這些處理器做一些常規的處理任務—-比如,判斷陷阱型別並做相應的轉交。
中斷分發
中斷分為硬體中斷和軟體中斷。
I/O 裝置通過中斷通知處理器,以請求自己需要的服務。
軟體中斷主要包括DPC(分發或者延遲過程呼叫中斷)和APC(一部過程呼叫中斷)。
- 中斷陷阱處理器。
核心安裝了中斷陷阱處理器來響應裝置的中斷。中斷陷阱處理器或者將控制權轉交給一個負責處理該中段的外部例程(ISR)(硬體),或者傳遞給一個響應該中斷的內部核心例程(軟體)。
硬體中斷處理
中斷分發表(IDC,interrupt dispatch table)
在系統引導的時候,Windows 會填充IDT,其中包含了指向核心中負責處理各個中斷和異常的例程的指標。IDT 同時配置了中斷和異常,比如0x30 對應時鐘中斷,0xe 對應的系統的頁面錯誤處理器。
每個處理器有單獨的IDT,比如,在一個多處理器系統中,每個處理器都會接收時鐘中斷,所有的處理器都使用該中斷來測量執行緒的時限,但是,只有一個處理器在響應該中斷的時候更新系統時鐘。硬體中斷控制器
外部I/O 中斷進入到中斷控制器的某一根線上。該控制器然後在一根線上中斷處理器。一旦處理器被中斷了,它詢問控制器以獲得此中斷請求(IRQ,interrupt request)。中斷控制器將IRQ轉換為一箇中斷號(即IDT 索引),然後將控制權傳遞給恰當的中斷分發例程。
X86 中斷控制器
- PIC(可程式設計中斷控制器)與APIC(高階可程式設計中斷控制器)
APC(15根中斷線)APIC(256根中斷線),當前大部分X86-PC 依賴於APIC,同時相容APC。APC 由幾個部件構成:
- 一個專門接收裝置中斷的I/O APIC
- 本地APIC
本地APIC 在一條私有的APIC 總線上接收來自I/O APIC 的中斷。- 與i8259A 相容的中斷控制器,負責將APIC 傳入轉換成與PIC 等價的訊號。
I/O APIC 負責實現中斷傳送演算法—–這些演算法由軟體來選擇(Windows 硬體抽象層或者HAL)—-既可以平衡各個處理器之間的裝置中斷負載,也可以設法利用位置特性,將裝置中斷遞交給剛剛承擔了前一個同樣型別中斷的同一個處理器。
X64 中斷控制器
* X64 版本Windows 必須執行在支援APIC 的系統上*
軟體中斷請求級別(IRQL)
- 雖然中斷控制器已經實現了一層中斷優先順序,Windows 強迫使用它自己的中斷優先順序方案,稱為中斷請求級別(IRQL,interrupt request level)。
X86中,核心內部使用0~30 代表IRQL。
X64 和 IA64 系統中,採用從0~15代表IRQL。- 數值越大,中斷的優先順序越高。
- HAL 將硬體中斷號對映為IRQL。
- 中斷的優先順序與執行緒排程優先順序的關係
中斷是按照優先順序處理的,高優先順序的中斷會搶佔低優先順序中斷的執行權。與執行緒排程優先順序比起來,呼叫優先順序是一個執行緒的屬性,而IRQL 則是中斷源(鍵盤、滑鼠)的屬性,而且,每個處理器有一個可隨著作業系統程式碼的執行而變化的IRQL 設定。每個處理器的IRQL 設定決定了該處理器可以接收哪些中斷,所謂的遮蔽中斷,就產生線上程IRQL 高於硬體中斷源的時候。只有核心模式執行緒有許可權修改執行緒IRQL,使用者模式執行緒一直執行在被動級別。- 延遲IRQL
為了避免訪問PIC 這樣一個比較慢的動作,使用了PIC 的HAL,實現了延遲IRQL。當IRQL 被提升時,HAL在內部記錄下新的IRQL,如果一個更低優先順序的中斷隨後發生,HAL 將中斷遮蔽值設定成對於第一個中斷較為正確的值,並且將更低優先順序的中斷延遲至IRQL 被降低下來為止。如果當IRQL 被提升時沒有更低優先順序的中斷髮生,HAL 不需要修改PIC。- 中斷髮生的時候,陷阱處理器將該處理器的IRQL提升至中斷源的IRQL,遮蔽(同一處理器的)底層中斷、以及同級的中斷。系統所有元件及核心都檢視讓IRQL 保持在被動級別上,以及時響應硬體中斷。
將中斷對映到IRQL
- windows 中,匯流排型驅動程式的裝置驅動程式確定它的總線上有哪些裝置,以及哪些中斷可以分配給某一個裝置。匯流排型驅動程式將這些資訊彙報給即插即用管理器,即插即用管理器在考慮了所有其它裝置的可接受的中斷分配方案後,確定為每個裝置分配哪個中斷。然後,它呼叫HAL 函式HalpGetSystemInterruptVector ,該函式將中斷對映到對應的IRQL。
預定義的IRQL
IRQL | 作用 |
---|---|
高階的IRQL | 核心在KebugCheckEx 中停止了系統並遮蔽了所有中斷的時候使用 |
電源失效 | 未被使用到過 |
處理期間的中斷 | 用於向另一個處理器請求執行一個動作,比如將一個DISPATCH_LEVEL 中斷排隊到佇列中以便為它排程到一個特定的執行緒來執行其任務、更新處理器的地址轉換快查緩衝區(TLB)的快取記憶體、系統關閉或者系統崩潰 |
時鐘 | 主要用於系統的時鐘,核心利用該中斷級別來跟蹤具體的時刻,以及為執行緒測量或分配CPU 時間 |
效能剖析 | 當核心的效能剖析功能被開啟的時候使用 |
裝置IRQL | 被用來對裝置中斷進行優先順序區分 |
DPC/APC | 由核心和裝置驅動驅動程式產生的軟體中斷 |
被動級別 | 是普通執行緒的執行時的設定,此時所有的中斷都允許發生 |
執行在DPC/Dispatch 級別或更高級別的程式碼,不能等待一個物件、只能訪問非換頁記憶體。—-此時執行緒進入等待,迫使排程器選擇另一個執行緒來執行,由於排程器在Dispatch 級別上和它的資料結構同步,因此它不能被呼叫來執行重新排程的操作。
中斷物件
- 中斷物件中包含了所有“供核心將一個裝置的ISR 與一個特定級別的中斷關聯起來而需要”的資訊,包括該ISR 的地址、該裝置中斷時所在的IRQL,以及核心中與該ISR 關聯的IDT 項當一箇中斷物件被初始化的時候,少量的彙編程式碼被從一箇中斷處理模板KiInterruptTemplate 拷貝過來,儲存在該物件中。當發生中斷的時候,這些程式碼將被執行。
- 中斷程式碼
上面講到的中斷程式碼,呼叫了實際的中斷分發器,通常是核心的KiInterruptDispatch(一箇中斷向量-一箇中斷物件) 或 KiChainedDispatch(一箇中斷向量-多箇中斷物件) 例程,並將指向該中斷物件的指標傳遞給它。
將一個ISR 與某個特定的中斷級別關聯起來,這一操作稱為連線一箇中斷物件,而將一個ISR 與IDT 項斷開關聯,稱為斷開一箇中斷物件。這些操作是通過呼叫核心函式IoConnectInterrupt 和 IoDisconnectInterrupt 來完成的。
使用中斷物件來註冊一個ISR ,可以避免裝置驅動程式直接操縱中斷硬體,也可以不必知道IDT 的內部細節。有助於建立可移植的裝置驅動程式。
軟體中斷
可以激發軟體中斷的任務包括
- 激發執行緒分發
- 並非時間緊迫的中斷處理
- 處理定時器到期
- 在特定執行緒的環境中非同步執行一個過程;
- 支援非同步I/O 操作
分發或者延遲過程呼叫(DPC)中斷
- 分發中斷的含義
當IRQL 在 DPC/Dispatch 或更高的時候,禁止了軟體中斷和執行緒分發動作。當核心檢測到應該進行分發的時候,請求一個DPC/Dispatch 級別的中斷,但是,因為IRQL >= DPC/Dispatch ,處理器保留該中斷,等核心完成了當前動作,它知道把IRQL 降低到DPC/Dispatch 一線,並且檢視一下是否有分發中斷正在等待處理。有的話,將IRQL 設定為 DPC/Dispatch,於是這些分發中斷可以得到處理了。利用軟體中斷來啟用執行緒分發器,這是一種將分發過程推遲到條件何時時才進行的方法。延遲過程呼叫的含義
參照上面的思路,我們可以發現,DPC 賦予作業系統一種能力:產生一箇中斷並在核心模式下執行一個系統函式。前面說過,Windows—在裝置驅動程式的配合下–企圖將IRQL 保持在低於裝置IRQL 的級別下。達到這一目的的一種方法是,讓裝置驅動程式ISR 執行最少最必要的工作來響應它們的裝置、將易變的中間狀態儲存起來,將資料傳輸或者其它非時間緊迫的中斷處理活動推遲到一個位於DPC/Dispatch IRQL 級別的DPC 上再執行。DPC 物件
每個處理器有一個DPC 佇列,用於儲存等待執行的DPC例程,DPC 有低中高三個優先順序,可以將DPC 物件放到任意的處理器上。DPC 物件是一種核心控制物件,僅核心可見,其最重要的資訊就是一個函式地址,在核心處理該DPC 時要呼叫該函式。DPC 執行的兩種方式
處理器的IRQL將要從DPC/Dispatch 或者更高的IRQL 降低到某個更低的IRQL,核心確保IRQL 保持在DPC/Dispatch 級別,抽空DPC 佇列,依次執行DPC 例程。
產生DPC 中斷的條件
DPC優先順序 | DPC被定為在ISR 的處理器上 | DPC 被定位在另一個處理器上 |
---|---|---|
低階 | DPC 佇列長度超過最大的DPC佇列長度值,或者DPC請求率小於最小的DPC 請求率 | DPC 佇列長度超過最大的DPC 佇列長度值,或者系統空閒 |
中級 | 總是激發 | DPC 佇列長度超過最大的DPC 佇列長度值,或者系統空閒 |
高階 | 總是激發 | 總是激發(通過傳送IPI) |
非同步過程呼叫(APC)中斷
APC 提供了一種在特定的使用者執行緒環境(從而也是特定程序的地址空間)中執行使用者程式和系統程式碼的途徑。(DPC 的執行環境是不確定的)
APC 物件
APC 是由一個核心控制物件來描述的,稱為APC 物件。APC 是執行緒相關的,當核心接收到請求,要將一個APC 排隊時,它將該APC 插入到將來執行此APC 例程的那個執行緒的佇列中。然後,核心請求一個APC 級別的軟體中斷,當該執行緒最終開始執行的時候,就會執行此APC。
有兩種型別的APC,核心模式和使用者模式。核心模式APC
核心模式APC 無需目標執行緒的干涉或者同意,就可以中斷該執行緒並執行一個過程。核心模式的APC有兩種型別:普通的和特殊的。將IRQL 提升到APC_LEVEL 或者呼叫KeEnterGuardedRegion 就可以禁止這兩種型別的APC。KeEnterCriticalRegion 可以禁止普通APC。* 執行體使用核心模式的APC 來完成那些必須要在特定執行緒的地址空間中才能完成的作業系統任務*使用者模式APC
ReadFileEx 和 WriteFileEx 函式允許使用者指定一個完成例程。當I/O 操作完成的時候該完成例程被呼叫。I/O 完成機制是通過在發起I/O 操作的執行緒裡插入一個APC 來實現的。- 可警告的等待狀態
只有當一個執行緒處於可警告的等待狀態,使用者模式APC 才可以交付給該執行緒。一個執行緒可以通過兩種方式進入該狀態:
等待一個物件控制代碼並指定它的狀態是可警告的(使用WaitForMultipleObjectsEx 函式)
直接測試一下是否有未處理的APC(使用SleepEx)。
這兩種情況下,如果有一個使用者模式的APC 正在等待處理,核心中斷該執行緒,將控制權傳遞給APC 例程,當APC 例程完成時恢復執行緒的執行。
核心模式APC 執行在APC 級別上,不同的是,使用者模式APC 執行在被動級別上。
APC 的交付可能導致等待佇列的重新排序。如果當一個APC 被交付時,執行緒正處於等待狀態,則在APC 例程完成之後,此等待動作將被重新發起或被重新執行。如果該等待動作尚未被解析,則執行緒返回到等待狀態,但現在,在它所等待的物件中,它位於等待列表的末尾。