1. 中斷被分爲同步中斷和異步中斷。同步中斷是cpu在執行指令過程中觸發的, 異步中斷是其他硬件設備在任意時間所引發的。
2. 英特爾手冊中將同步中斷稱爲異常,異步中斷稱爲中斷。
3. 中斷一般由外部io設備觸發,異常一般分爲兩種:1. 由於程序錯誤引發。 2. 異常的執行情況或請求,例如頁故障和系統調用。
4. 內核處理中斷時,將當前進程的eip和cs寄存器放入內核棧中,然後載入中斷處理程序的地址。
5. 中斷處理程序執行的是不是另外一個進程,而是另外一條執行路徑,因此,比進程切換的開銷要小一些。
6. 中斷處理是可以嵌套的,但是在某些情況下,執行中斷處理程序的時候,需要關閉中斷。
7. 中斷分爲可屏蔽中斷和不可屏蔽中斷,一般的IO設備產生的中斷是可屏蔽中斷,緊急情況下的(硬件故障)屬於不可屏蔽中斷。
8. 異常又分爲cpu檢測到的異常和軟件觸發的異常。
9. cpu檢測到的異常分爲三類:
(1) faults, 一般是可以恢復的,再次執行eip。(頁故障)
(2) traps, 恢復後, 執行eip + 1。 (debuger)
(3) aborts, 無法恢復,一般會殺死相應進程。(硬件故障)
10. 軟件觸發的異常一般被當作traps處理,又叫軟中斷,一般應用於實現系統調用和調試。
11. 中斷被屏蔽後不會丟失,當中斷不被屏蔽後,pic會儘快地通知cpu。
12. 關閉中斷會導致pic觸發的中斷被忽略,eflags中的IF位清零。彙編指令cli和sti分別用來關閉和打開中斷。
13. 在SMP系統中,使用IOAPIC來對外部的中斷進行路由,共有24箇中斷線,並且通過重定向表來確定中斷向量和優先級。
14. 中斷分發到cpu分爲兩類:
(1)靜態分發,在重定向表中配置接收中斷的cpu。
(2)動態分發,根據TPR寄存器的值,計算當前運行進程的優先級。TPR寄存器在每次進程切換的時候刷新。
15. APIC 系統可以讓cpu之間發送IPI。
16. IOAPIC可以被配置爲兩種模式:
(1) 經典的8259, LAPIC被禁止。
(2) 標準的IOAPIC, 所有的外部中斷都通過IOAPIC進行路由。
17. x86系統大約有20個異常,20-31爲intel保留未來使用。
18. IDT用來關聯中斷(異常)和對應的處理函數。IDT必須在使能中斷前設置好。
19. IDT的每一項對應一箇中斷或異常,每一個項8個字節,因此最多需要256 × 8 = 2048 bytes。
20. IDT包含三種描述符
(1)Task gate, 包含當中斷髮生時要切換到的TSS選擇子。
(2)Interrupt gate, 包含中斷處理程序的段選擇符和偏移,當轉移相應的段之後, IF位被清零,後續中斷被禁止。
(3) Trap gate,和中斷門類似,但是轉移之後,中斷不禁止。
21. linux用中斷門來處理中斷,用陷阱門來處理異常。
22. 發生中斷時,內核會檢查是否需要切換運行級別,如果需要的話,從tss中讀取ss和esp並且載入相應寄存器,在新的棧中保存之前運行級的ss和esp。如果是faults的話,把eip和cs設置成引起fault的地址,方便再次恢復執行。最後把eflags, cs, eip也保存在棧中(如果有錯誤碼的話,也保存),然後跳轉到中斷處理函數。
23. 中斷結束後,調用iret,將棧上的cs,eip,eflags載入(有錯誤碼的話,必須先彈出),如果中斷處理程序的運行級別和CPL不同,載入ss和esp(之前運行級),最後檢查ds,es,fs,gs, 如果發現運行級別小於CPL,清除該寄存器,防止用戶態訪問內核地址空間。
24. 中斷嵌套要求在處理中斷過程中,不能發生進程切換,因爲恢復嵌套所需要的信息都在當前進程的內核棧中。
25. 大部分異常發生在用戶態,內核態只有可能發生缺頁異常。因此,異常最多有兩個嵌套,先是系統調用,然後是缺頁異常。
26. 中斷處理可以搶佔其他中斷處理和異常處理,異常處理從來不會搶佔中斷處理。中斷處理函數中不會出現導致缺頁異常的操作,因爲它會帶來進程切換。
27. 在實模式下,BIOS會初始化IDT,但是linux不使用任何BIOS的函數, 因此,linux會將IDT移動到另外的內存地址並且再次初始化。
28. IDT被保存在idt_table 中,有256個選項,idt_descr包含IDT表的大小和起始地址。
29. 內核先將IDT表初始化爲一個特定的處理函數,然後在進行第二次初始化的時候,填入正確的處理函數。
30. 內核利用異常來管理硬件資源,主要包括兩個例子,一個是用device not available 和 cr0中的TS位來刷新浮點寄存器的值,令一個是頁故障。
31. 異常處理的流程包括三個步驟:
(1) 在內核棧保存大部分的寄存器。(用匯編編寫)
(2) 處理異常。
(3) 通過ret_from_exception來退出異常處理函數。
32. trap_init函數來填充IDT表項,它會調用set_trap_gate, set_intr_get, set_system_gate, set_intr_gate等函數。
33. Double fault 異常是通過task_gate 來處理的,當異常發生時,內核不信任當然的esp值,因此會通過IDT(第8個)找到任務門的描述符,它指向一個特殊的TSS
(GDT的第32個項), 然後載入eip和esp,最終在私有的堆棧上執行doublefault_fn。
34. 異常入口函數首先選擇性地壓棧一個error code ,然後將異常處理函數的地址壓棧,最後跳到error_code 標籤處執行。error_code 執行以下操作:
(1) 保存可能用到的寄存器到堆棧中。
(2) 清除DF flags, 保證string操作時edi和esi的自增。
(3)把error code 複製到 edx中,在原來的位置寫入-1,這個值用來區分0x80(系統調用)和其他的異常。
(4) 將do_handler_name 載入edi中,在原來的位置寫入es的內容。
(5) 將當前的內核棧頂載入eax中。
(6) 將用戶態的ds載入ds和es寄存器。
(7) 調用edi中的處理函數。
35. 大部分的異常處理函數會調用do_trap將hardware error code和 exception vector 保存到當前進程中,然後發送一個相應的信號給當前進程。信號處理可以在用戶態也可以在內核態,在內核態的話, 一般就是殺死進程。
36. 內核異常處理會檢查異常是發生在用戶態還是內核態,如果是在內核態,會繼續檢查是否由於錯誤的參數傳遞給系統調用,其他的內核異常會導致內核panic。
37. 異常處理結束後,會跳轉到ret_from_exception函數來返回。
38. 中斷處理中,發送信號給當然進程是沒有意義的,因爲當然進程並不一定是觸發中斷的進程。
39. 一箇中斷處理函數需要靈活地服務多個設備,因爲向量可以被多個設備共享,所以對於中斷共享的方式,多箇中斷服務程序會依次執行,直到確認中斷源。
40. 中斷處理過程中,相應的中斷會被自動屏蔽,並且不能調用引起阻塞的函數。
41. 對於IO中斷來說主要有以下步驟:
(1)保存中斷號和寄存器到內核棧中。
(2) 向pic確認中斷,允許中斷排隊。
(3)依次執行關聯該中斷號的所有中斷服務程序。
(4) 通過ret_from_intr返回。
42. 中斷向量:
(1) 0-19: 不可屏蔽中斷和異常
(2) 20-31: 英特爾保留。
(3) 32-127: 外部中斷。
(4) 128: 系統調用。
(5) 129-238: 外部中斷。
43. 中斷處理的入口處,會將n-256壓棧,這是個負數(正數用來表示系統調用), 然後跳到common_interrupt。
44. common_interrupt 會首先保存寄存器到堆棧,除了eflags, cs, eip, ss, 和 esp, 這些已經被處理器自動保存了。之後把用戶態的ds載入ds和es, 最後調用do_irq,
regs指針保存在eax中。
45. enable_irq函數會檢測是否有中斷被遺漏:當IRQ_PEDING被設置,說明中斷被確認但是沒有被服務,它會調用hw_resend_irq來重新產生一個自我中斷,並設置IRQ_REPLAY。
46. handle_IRQ_event函數:
(1) 如果之前關閉了中斷,重新打開。
(2) 依次執行每個中斷服務程序。
(3)關閉中斷。
(4) 返回中斷服務程序的返回值。(0表示沒有識別該中斷,否則爲1)
47. 當設備不能夠共享irq時,可以動態分配irq。設備驅動調用request_irq, 之後分配一個irqaction,並調用setup_irq來鏈接到相應的irqaction 鏈表裏。
48. 可延時函數: softirq 和 tasklet 執行在中斷上下文中。
49. softirq 和 tasklet的區別:
(1) softirq是靜態分配的,tasklet可以靜態也可以動態分配。
(2)softirq可以在幾個cpu中並行執行,因此必須是可重入的。tasklet是序列化的,不需要可重入。
50. 可延時函數的激活和執行必須在同一個cpu上。
51. open_softirq 用來初始化softirq,raise_irq 用來激活softirq。如果in_interrupt返回0,就調用wake_softirq來激活當前cpu的ksoftirqd(如果必要的話)。
52. 檢查是否有等待的softirq是週期性的,一般有如下幾個檢查點:
(1) 調用local_bh_enable來使能當前cpu的softirq。
(2)當do_irq結束IO中斷處理後調用irq_exit宏的時候。
(3) 在 I/O APIC中,當smp_apic_timer_interrupt函數完成當前的時間中斷處理函數時。
(4) 在SMP中,當一個cpu完成 CALL_FUNCTION_VECTOR處理器間中斷時。
(5) 當任意一個ksoftirqd內核線程被喚醒的時候。
53. work queue 不同於softirq 和tasklet,可以運行在進程上下文中,是由kworker內核線程來運行,二中都不能訪問用戶空間內存。
54. 當內核從中斷或異常返回時,需要處理以下事情:
(1)如果沒有更多的內核路徑,應該切回到用戶態。
(2)如果有等待的調度請求,內核必須去實施一次調度,否則就返回當前進程。
(3)如果有信號要發送給當前進程,內核必須去處理這個信號。
(4)等等。
55. ret_from_intr 和 ret_from_exception基本流程相同,差別就在於是否開啓內核搶佔,如果開啓搶佔的話,ret_from_exception會關閉本地的中斷。
56. 不論是返回到內核還是用戶態,如果開啓了內核搶佔,都需要去檢查是否有調度請求,如果有的話,需要實施調度。