深入理解linux內核讀書筆記 (第四章)

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. 不論是返回到內核還是用戶態,如果開啓了內核搶佔,都需要去檢查是否有調度請求,如果有的話,需要實施調度。

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