系統(tǒng)中斷
?中斷機制是現(xiàn)代計算機系統(tǒng)中的基本機制之一,他完成了對計算機各個事件(如時鐘、鍵盤等)響應工作。
?首先,有兩類事件能引起x86掛起當前指令流的執(zhí)行并響應事件: 異常(Exception) 和 硬件中斷(Interrupt)。其中異常又被稱為 軟中斷,他用于處理計算機執(zhí)行過程中的一些異常情況,如除0操作、溢出、棧錯誤等情況,這些中斷 不可屏蔽。而硬件中斷又分為了外部中斷(可屏蔽)和內部中斷(不可屏蔽),外部中斷一般就是計算機外設所發(fā)出的中斷(如敲擊了鍵盤等),它們由如下圖所示的兩個級聯(lián)的8259A芯片所控制 ;內部中斷就是計算機內部硬件出現(xiàn)的像硬件出錯(掉電、校驗、傳輸)等情況所發(fā)出的中斷。上述所有的這些中斷,都被統(tǒng)一的編排在了接下來將介紹的中斷向量表中。
?8086機器中的中斷向量表在80386機器中被改為了中斷描述符表(interrupt description table,IDT),它存放著用于描述各個中斷的表項,每個都由8字節(jié)組成,包括了該中斷的地址、特權級等信息。這些表項又被稱為了門描述符(Gate Descriptor),“門”的含義是當中斷發(fā)生時必須先通過這些門,然后才能進入相應的中斷處理程序(Interrupt Service Routine, ISR)。
?如前文所述,所有的中斷(或異常)都被 統(tǒng)一 的編排在了IDT中,即這個IDT包含了異常向量和硬件中斷向量,而IDT中一共有256個門描述符,每個門描述符描述一個中斷(或異常),同時就對應著一個中斷(或異常)的處理程序。這256個中斷或異常向量的分配如下:
* 從0~31 的向量對應于異常和非屏蔽中斷;
* 從32~47 的向量(即由I/O 設備引起的中斷)分配給屏蔽中斷;
* 剩余的從48~255 的向量用來標識軟中斷。Linux 只用了其中的一個(即128 或0x80向量)用來實現(xiàn)系統(tǒng)調用(trap)。當用戶態(tài)下的進程執(zhí)行一條int 0x80 匯編指令時,CPU 就切換到內核態(tài),并開始執(zhí)行system_call() 內核函數(shù)。
?我們先說異常的處理。在這256個處理程序中,前32個都是非屏蔽中斷,0~20號中斷處理程序又都用于CPU的異常中斷(即軟中斷),隨后的12個中斷處理程序被Intel保留,詳細情況如下:
// 0~31個都是非屏蔽中斷
// 聲明中斷處理函數(shù) 0-19 屬于 CPU 的異常中斷
// ISR:中斷處理程序(interrupt service routine)
void isr0(); // 0 #DE 除 0 異常
void isr1(); // 1 #DB 調試異常
void isr2(); // 2 NMI
void isr3(); // 3 BP 斷點異常
void isr4(); // 4 #OF 溢出
void isr5(); // 5 #BR 對數(shù)組的引用超出邊界
void isr6(); // 6 #UD 無效或未定義的操作碼
void isr7(); // 7 #NM 設備不可用(無數(shù)學協(xié)處理器)
void isr8(); // 8 #DF 雙重故障(有錯誤代碼)
void isr9(); // 9 協(xié)處理器跨段操作
void isr10(); // 10 #TS 無效TSS(有錯誤代碼)
void isr11(); // 11 #NP 段不存在(有錯誤代碼)
void isr12(); // 12 #SS 棧錯誤(有錯誤代碼)
void isr13(); // 13 #GP 常規(guī)保護(有錯誤代碼)
void isr14(); // 14 #PF 頁故障(有錯誤代碼)
void isr15(); // 15 CPU 保留
void isr16(); // 16 #MF 浮點處理單元錯誤
void isr17(); // 17 #AC 對齊檢查
void isr18(); // 18 #MC 機器檢查
void isr19(); // 19 #XM SIMD(單指令多數(shù)據(jù))浮點異常
// 20-31 Intel 保留
//...
?其次再說硬件中斷的處理。在前面的8259A芯片的示意圖中,可能你已經(jīng)發(fā)現(xiàn),我們給每一個外部中斷(可屏蔽中斷)都賦予了一個編號,這16個編號被稱作中斷請求號碼(Interrupt Request, IRQ)。它們實際上就是IDT中的32~47號向量,詳細情況如下:
// IRQ 定義
// 定義IRQ
#define IRQ0 32 // 電腦系統(tǒng)計時器
#define IRQ1 33 // 鍵盤
#define IRQ2 34 // 與 IRQ9 相接,MPU-401 MD 使用
#define IRQ3 35 // 串口設備
#define IRQ4 36 // 串口設備
#define IRQ5 37 // 建議聲卡使用
#define IRQ6 38 // 軟驅傳輸控制使用
#define IRQ7 39 // 打印機傳輸控制使用
#define IRQ8 40 // 即時時鐘
#define IRQ9 41 // 與 IRQ2 相接,可設定給其他硬件
#define IRQ10 42 // 建議網(wǎng)卡使用
#define IRQ11 43 // 建議 AGP 顯卡使用
#define IRQ12 44 // 接 PS/2 鼠標,也可設定給其他硬件
#define IRQ13 45 // 協(xié)處理器使用
#define IRQ14 46 // IDE0 傳輸控制使用
#define IRQ15 47 // IDE1 傳輸控制使用
// 外部中斷
// 聲明 IRQ 函數(shù)
// IRQ:中斷請求(Interrupt Request)
void irq0(); // 電腦系統(tǒng)計時器
void irq1(); // 鍵盤
void irq2(); // 與 IRQ9 相接,MPU-401 MD 使用
void irq3(); // 串口設備
void irq4(); // 串口設備
void irq5(); // 建議聲卡使用
void irq6(); // 軟驅傳輸控制使用
void irq7(); // 打印機傳輸控制使用
void irq8(); // 即時時鐘
void irq9(); // 與 IRQ2 相接,可設定給其他硬件
void irq10(); // 建議網(wǎng)卡使用
void irq11(); // 建議 AGP 顯卡使用
void irq12(); // 接 PS/2 鼠標,也可設定給其他硬件
void irq13(); // 協(xié)處理器使用
void irq14(); // IDE0 傳輸控制使用
void irq15(); // IDE1 傳輸控制使用
?這樣,用戶程序就可通過INT指令加一個中斷向量向系統(tǒng)發(fā)出一個中斷請求了,如DOS匯編指令中使用 INT 21 就能調用功能豐富的21號中斷服務。