IA-32 架構(gòu)下異常和中斷的處理

前言:上一次我們說了說,異常和中斷是什么,現(xiàn)在我們說說IA-32 架構(gòu)下異常和中斷的處理

還記得這個(gè)圖嗎?

異常和中斷的處理方式很像,都是進(jìn)入內(nèi)核態(tài)。所以兩者的處理方式很相同。在 IA-32 架構(gòu)下有 256 種不同的異常和中斷。

每個(gè)異常和中斷都有唯一編號(hào),稱之為中斷類型號(hào),如類型號(hào) 0,是除法除以 0。而且,每一個(gè)異常和中斷都有其對應(yīng)的異常處理程序和中斷服務(wù)程序,其入口地址放在一個(gè)專門的「中斷向量表」或者「中斷描述符」中

前 32 個(gè)類型(0~31)留給 CPU,剩下的(32~255)由操作系統(tǒng)定義。

通過 int n(31 < n < 256),使 CPU 自動(dòng)轉(zhuǎn)到 OS 給出的中斷服務(wù)程序執(zhí)行

一共有兩種不同的表:「中斷向量表」和「中斷描述符」,原因是,我們從開機(jī)到由操作系統(tǒng)接管機(jī)器經(jīng)歷了兩種模式:

實(shí)地址模式-------->保護(hù)模式

在實(shí)模式中存儲(chǔ)中斷程序入口表的叫做:「中斷向量表」,而在保護(hù)模式存儲(chǔ)中斷程序入口表的叫做:「中斷描述符表」

現(xiàn)在分別來介紹兩種表:

實(shí)地址模式:中斷向量表

我們知道:在實(shí)地址模式下通過 cs(16 位) ip(16 位) 來確定地址(cs << 4 + ip)。尋址空間只有 1M(20 位)。

中斷向量表位于 0000H~03FFH。共 256 組,每組占四個(gè)字節(jié) CS:IP 。

(就像這樣)

是誰!讓我的機(jī)器有了「中斷向量表」

都是 BIOS!!!

BIOS 干了啥?

  • 開機(jī)后系統(tǒng)首先在實(shí)地址模式下工作(只有 1MB 尋址空間)
  • 開機(jī)過程中 BIOS 在實(shí)地址模式下準(zhǔn)備「中斷向量表」和「中斷服務(wù)程序」
  • BIOS 程序檢查顯卡,鍵盤和內(nèi)存等等,并在 00000H~003FFH 區(qū)建立中斷向量表,并在主存中準(zhǔn)備了中斷服務(wù)程序
  • BIOS 利用 INT 指令執(zhí)行特定的中斷服務(wù)程序把OS從磁盤加載到內(nèi)存中。例如,BIOS 可通過執(zhí)行 int 0x19 指令來調(diào)用中斷向量 0x19 對應(yīng)的中斷服務(wù)程序,將啟動(dòng)盤上的 0 號(hào)磁頭對應(yīng)盤面的 0 磁道 1 扇區(qū)中的引導(dǎo)程序裝入內(nèi)存
  • BIOS(Basic Input/Output System)是基本輸入/輸出系統(tǒng)的簡稱,是針對具體主板設(shè)計(jì)的,與安裝的操作系統(tǒng)無關(guān)
  • BIOS 包含各種基本設(shè)備驅(qū)動(dòng)程序,通過執(zhí)行 BIOS 程序,基本設(shè)備驅(qū)動(dòng)程序以中斷服務(wù)程序的形式被加載到內(nèi)存,以提供基本 I/O 系統(tǒng)調(diào)用
  • 一旦進(jìn)入保護(hù)模式,就不再使用BIOS

保護(hù)模式:中斷描述符表

保護(hù)模式下,通過「中斷描述符表」獲異常處理或中斷服務(wù)程序入口地址。

中斷描述符表 (Interrupt Descriptor Table,IDT)是 OS 內(nèi)核中的一個(gè)表,共有 256 個(gè)表項(xiàng),每個(gè)表項(xiàng)占 8 個(gè)字節(jié),IDT 共占用 2KB,由 IDTR 存放 IDT 在內(nèi)存的首地址,每一個(gè)表項(xiàng)是一個(gè)中斷門描述符,陷阱門描述符,或者任務(wù)門描述符

下面介紹中斷描述符的格式

有了現(xiàn)在的知識(shí),我們就可以來學(xué)習(xí)IA-32中異常和中斷的處理

IA-32 中異常和中斷的處理

每條指令 CPU 都會(huì)根據(jù)執(zhí)行情況判斷內(nèi)部是否發(fā)生了異常事件,在指令結(jié)束后判斷是否發(fā)生了外部中斷請求

由此可見,異常事件和中斷請求的 檢測 都是在某一條指令執(zhí)行過程中進(jìn)行的,顯然由硬件完成

在 CPU 根據(jù) CS 和 EIP 取下條指令之前,會(huì)根據(jù)檢測的結(jié)果判斷是否進(jìn)入中斷響應(yīng)階段

異常和中斷的響應(yīng)也都是在某一條指令執(zhí)行過程中或執(zhí)行結(jié)束時(shí)進(jìn)行的,顯然也由硬件完成

現(xiàn)在開始敘述所有IA -32 中異常和中斷響應(yīng)過程

  • 確定中斷類型號(hào) i(int i),從 IDTR 指向的 IDT 中取出第 i 個(gè)表項(xiàng) IDTi
  • 從 IDTi 中選擇段選擇符,從 GDTR 中得到 GDT,再從 GDT 中取出相應(yīng)段描述符,得到對應(yīng)異常或中斷處理程序所在段的 DPL、基地址等信息。Linux 下中斷門和陷阱門對應(yīng)的即為內(nèi)核代碼段,所以 DPL 為 0,基地址為 0
  • 若 CPL < DPL 或編程異常 IDTi 的 DPL<CPL,則發(fā)生 13 號(hào)異常。Linux 下,前者不會(huì)發(fā)生。后者用于防止惡意程序模擬 INT n 陷入內(nèi)核進(jìn)行破壞性操作
  • 若 CPL≠DPL,則從用戶態(tài)換至內(nèi)核態(tài),以使用內(nèi)核棧。切換棧的步驟:
    • 讀 TR 寄存器,以訪問正在運(yùn)行的用戶進(jìn)程的 TSS 段
    • TSS 段中保存的內(nèi)核棧的段選擇符和棧指針分別裝入寄存器 SS 和 ESP,然后在內(nèi)核棧中保存原來用戶棧的 SS 和 ESP
  • 若是故障,則將發(fā)生故障的指令的邏輯地址寫入 CS 和 EIP,以使處理后回到故障指令執(zhí)行。其他情況下,CS 和 EIP 不變,使處理后回到下條指令執(zhí)行
  • 在當(dāng)前棧中保存 EFLAGS、CS 和 EIP 寄存器的內(nèi)容(斷點(diǎn)和程序狀態(tài))
  • 若異常產(chǎn)生了一個(gè)硬件出錯(cuò)碼,則將其保存在內(nèi)核棧中
  • 將 IDTi 中的段選擇符裝入 CS,IDTi 中的偏移地址裝入 EIP,它們是異常處理程序或中斷服務(wù)程序第一條指令的邏輯地址(Linux中段基址 = 0)

下個(gè)時(shí)鐘周期開始,從 CS:EIP 所指處開始執(zhí)行異常或中斷處理程序!

什么是 TSS

內(nèi)核中的 TSS 段記錄了每個(gè)進(jìn)程的狀態(tài)信息,例如,每個(gè)進(jìn)程對應(yīng)的頁表、task 和 mm 等結(jié)構(gòu)信息。

紅色那塊就是 TSS 段。

從中斷程序中跳出來

中斷或異常處理程序最后一條指令是 IRET。CPU 在執(zhí)行 IRET 指令過程中完成以下工作

  • 從棧中彈出硬件出錯(cuò)碼(保存過的話)、EIP、CS 和 EFLAGS
  • 檢查當(dāng)前異常或中斷處理程序的 CPL 是否等于 CS 中最低兩位,若是則說明異常或中斷響應(yīng)前、后都處于同一個(gè)特權(quán)級(jí),此時(shí),IRET 指令完成操作。否則,再繼續(xù)完成下一步工作。
  • 從內(nèi)核棧中彈出 SS 和 ESP,以恢復(fù)到異常或中斷響應(yīng)前的用戶級(jí)進(jìn)程所使用的棧。
  • 檢查DS、ES、FS 和 GS 段寄存器的內(nèi)容,若其中有某個(gè)寄存器的段選擇符指向一個(gè)段描述符且其 DPL 小于 CPL,則將該段寄存器清 0。這
    是為了防止惡意應(yīng)用程序(CPL = 3)利用內(nèi)核以前使用過的段寄存器(DPL=0)來訪問內(nèi)核地址空間。

執(zhí)行完 IRET 指令后,CPU 回到原來發(fā)生異常或中斷的進(jìn)程繼續(xù)執(zhí)行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容