七. 中斷

簡介

中斷是指CPU獲知了計算機中發生的某些事,CPU暫停正在執行的程序,轉而去執行處理該事件的程序,當這段程序執行完了之後,CPU繼續執行剛才的程序。

通過中斷可以極大的提高CPU的執行效率,如果沒有中斷,在處理器與外部設備通信時,他必須在向該設備發送指令後進入忙等待,反覆輪詢該設備是否就緒,這樣就浪費了大量處理器的執行周期。引入中斷之後,當處理器發出設備請求後就可以立即返回處理其他任務,而當設備動作完成之後,發送中斷信號給處理器,後者就可以在完成動作之後回來獲取處理結果

中斷分類

按照中斷事件來源分類,可以把中斷分為外部中斷和內部中斷

外部中斷

外部中斷是指來自CPU外部的中斷,而外部的中斷源通常時某個硬體,所以外部的中斷也叫硬體中斷

由於外部設備在種類和數量上都非常多,CPU不可能為每個外部設備專門設計一個介面去處理他的中斷信號。所以只能提供統一的介面作為中斷信號的公共線路,所有來自外設的中斷信號都共享公共線路連接都CPU。

其介面示意圖如下

從INTR引腳收到的中斷都是不影響系統運行的,可以隨時處理,他不會影響到CPU的執行。也稱為可屏蔽中斷。可以通過eflag中的if位將所有這些外部中斷屏蔽

從NMI引腳收到的中斷,通常是由於系統發生致命錯誤所導致的,比如電源掉電,內存讀寫錯誤等等。

內部中斷

內部中斷可分為軟中斷和異常

軟中斷是由軟體主動發起的中斷,他是軟體主動發起的。

發起軟中斷有以下幾種方式

  1. int 8位的立即數 通過它,我們可以發起系統調用,8位的立即數可以表示256種中斷。處理器支持的中斷也是256種
  2. int3 調試斷點指令,在調式程序的時候,我們所下的斷點就是通過他來實現的。通過調試器fork子進程,在斷點處用int3替換原指令,從而使子進程調用int3觸發中斷
  3. into 中斷溢出指令,只有在eflags中的OF位為1的情況下才會被觸發
  4. bound 檢查數組索引越界指令
  5. ud2 未定義指令,CPU遇到無效指令時觸發該中斷

異常是在指令執行期間CPU內部產生的錯誤引起的,由於是運行時錯誤,他不受eflags中的IF位的影響

異常按照輕重的等級,可分為以下三種

  1. Fault,也稱故障。屬於可被修復的一種類型,當發生此類異常時,CPU將機器狀態恢復到異常之前的狀態,之後調用中斷處理程序,通常都能夠被解決。缺頁異常就屬於此種異常
  2. Trap,也稱陷阱。此異常通常在調試中。
  3. Abort,也稱終止。程序發生了此類異常通常就無法繼續執行下去,操作系統會將此程序從進程表中去除。

中斷描述符表

中斷描述符表是保護模式下用於存儲中斷處理程序入口的表,當CPU接受到一個中斷時,需要根據該中斷的中斷向量號在此表中檢索對應的描述符,在該描述符中找到中斷處理程序的起始地址,然後執行中斷處理程序

門描述符

在中斷描述符表中存儲的不只是中斷描述符,還包括任務門描述符和陷阱門描述符。IDT中只有這種稱為門的描述符,那麼就要了解一下這些門的具體結構

中斷門包含了中斷處理程序所在的段選擇子和段內偏移地址,當通過此方式進入中斷後,標誌寄存器eflags中的IF位自動置0,避免中斷嵌套。linux就是利用的中斷門實現的系統調用,也就是int 0x80。

中斷門只允許存放在IDT中,而其他的門描述符則不然。

在描述符中,通過S欄位+type欄位來確定描述符的具體類型

S欄位為1時代表系統段,為0時代表數據段。此處的門描述符全部處於系統段下。

DPL表示描述符特權級


中斷的處理過程

  1. 處理器根據中斷向量號定位中斷門描述符
  2. 處理器進行特權級檢查
  3. 執行中斷處理程序

這裡重點要說一下特權級檢查的過程。

為了防止3特權級下的用戶主動調用只為內核服務的程序,當前的特權級必須經受的住中斷門的考驗,得「進入門內」。進門一般分為兩步,「跨過門檻」,「進入門框」

「跨過門檻」的過程其實是檢查 當前特權級(CPL)門描述符特權級(DPL) 的大小。要求 CPL許可權大於等於DPL,在數值上 CPL<=DPL,「門檻」檢查才通過,特權級的數值越小代表特權級越高。否則拋出異常

「進入門框」的過程實質是檢查 CPL門描述符中選擇子對應的目標代碼段的DPL,要求 CPL許可權小於目標代碼段的DPL,在數值上 CPL>目標代碼段DPL,此處不能相等。

如果該中斷是一個軟中斷,也就是通過 int n, int3, into等引發的中斷,執行上述兩步的特權級檢查

如果該中斷是外部設備中斷或者異常,只需要執行第二步的特權級檢查

中斷處理過程的示意圖:

中斷髮生時的壓棧

在中斷髮生之後,處理器要去執行中斷處理程序,該中斷處理程序是通過中斷門描述符中保存的代碼段選擇子和段內偏移找到的,也就是說需要重新載入段寄存器,那麼為了能在中斷處理完了之後還能返回當前進程,就必須保存當前進程的 CS:EIP,保存的地方當然就是中斷處理程序的棧中了。因為中斷可以在任意特權級下發生,所以當前進程的EFLAGS寄存器同樣需要保存,如果涉及到特權級的變化,還需要壓如SS和ESP寄存器

通過一副圖來詳細解釋壓棧的情況與順序

圖A、B:在發生中斷是通過特權級的檢測,發現需要向高特權級轉移,所以要保存當前程序棧的SS和ESP的值,在這裡記為ssold, espold,然後在新棧中壓入當前程序的eflags寄存器

圖C、D:由於要切換目標代碼段,這種段間轉移,要對CS和EIP進行備份,同樣將其存入新棧中。某些異常會有錯誤碼,用來標識異常發生在哪個段上,對於有錯誤碼的情況,要將錯誤碼也壓入棧中。

入棧說完了,在中斷執行完了返回的時候通過指令 iret 完成,這個指令專門用於從中斷返回

iret 指令會從棧頂依次彈出EIP、CS、EFLAGS,根據特權級的變化還有ESP、SS。但是該指令並不驗證數據的正確性,而且他從棧中彈出數據的順序是不變的,也就是說,在有error_code的情況下,iret返回時並不會主動跳過這個數據,需要我們手動進行處理


推薦閱讀:

TAG:操作系統內核 | 中斷 | Linux內核 |