【自制作業系統08】中斷
由於中斷這塊的知識和程式碼都佔較大篇幅,因此分成兩章來講,本章不包含任何中斷的程式碼,只講理論部分,以及中斷的大概流程。程式碼實現部分由下一章來講解
【自制作業系統09】中斷的程式碼實現
一、到目前為止的程式流程圖
為了讓大家清楚目前的程式進度,畫了到目前為止的程式流程圖,如下。
二、什麼是中斷
這裡我們先從形象的角度來描述,中斷就是讓作業系統停止手中正在進行的工作,先把中斷訊號對應的處理程式執行完畢,再回到之前的程式中繼續進行,這樣一個機制。
一個很形象的說法是,我們的作業系統就是 中斷驅動 的,可以把作業系統簡單理解為一個 死迴圈,無時無刻不在等待中斷的來臨,被動 地執行相應的任務。
while(true){ 作業系統程式碼 }
三、中斷的分類
外部中斷
外部中斷通過兩個引腳連線到 CPU 上,一個是可遮蔽中斷 INTR,一個是不可遮蔽中斷 NMI
- INTR:硬碟、印表機、網絡卡等裝置發出的中斷訊號,可通過 eflags 暫存器的 IF 位將所有這些外部裝置的中斷遮蔽
- NMI:電源掉電、記憶體讀寫錯誤、匯流排奇偶校驗錯誤等災難性的錯誤,不可遮蔽,CPU 必須立刻處理
對於可遮蔽中斷,Linux 的處理方式是分成 上半部 和 下半部。上半部執行時關閉中斷,立刻執行完畢;下半部執行時開啟中斷,此時如果有其他中斷進來,則讓給其他中斷(也是上半部執行完畢)。
內部中斷
內部中斷可分為 軟中斷 和 異常,二者均是不可遮蔽的(即不受 eflags 的 IF 位影響)
- 軟中斷:就是軟體發起的中斷,最常見的也是我們之後進行系統呼叫的,就是 int 8位立即數,可表示 256 中中斷。還有一些不常用的,甚至可以叫做異常,下面簡單列出
- int3:中斷向量號3,除錯斷點指令
- into:中斷向量號4,中斷溢位指令
- bound:中斷向量號5,檢查陣列索引越界指令
- ud2:中斷向量號6,未定義指令,常用於軟體測試中主動發起這個中斷
- 異常:指令執行期間 CPU 內部產生的錯誤引起,如分母為 0 將發起 6 號中斷(異常),未定義的指令發起 6 號中斷
- Fault(故障):可恢復的錯誤。發生此中斷時,CPU 將機器狀態恢復到異常之前的狀態,之後呼叫中斷處理程式,結束後返回。常見的如 缺頁異常
- Trap(陷阱):有意的異常。通常是除錯程式中用 int3 指令主動觸發。
- Abort(終止):不可恢復的異常。直接將此程式從程序表中去掉。
四、中斷號
我們知道一箇中斷對應著一個 中斷號(中斷向量號),下面列表說明
中斷號 | 含義 | 來源 | 型別 | 是否有錯誤碼 |
---|---|---|---|---|
0 | divide error | DIV and IDIV instructions | Fault | 無 |
1 | debug | any code or data reference | Fault/Trap | 無 |
2 | NMI Interrupt | NMI | Interrupt | 無 |
3 | Breakpoint | INT3 instruction | Trap | 無 |
4 | Overflow | INTO instruction | Trap | 無 |
5 | bound range exceeded | BOUND instruction | Fault | 無 |
6 | invalid opcode | UD2 instruction or reserved opcode.1 | Fault | 無 |
7 | device not available | floationg-point or WAIT/FWAIT instruction | Fault | 無 |
8 | double fault | any instruction that can generate an exception, an NMI, or an INTR | Fault | Y(0) |
9 | CoProcessor Segment Overrun | Floating-point instruction.2 | Fault | 無 |
10 | invalid TSS | task switch or TSS access | Fault | Y |
11 | segment not present | loading segment registers or accessing system segments | Fault | Y |
12 | stack segment fault | stack operations and SS register loads | Fault | Y |
13 | general protection | any memory reference and other protection checks | Fault | Y |
14 | page fault | any memory reference | Fault | Y |
15 | reserved | |||
16 | floating-point error | floating-point or WAIT/FWAIT instruction | Fault | 無 |
17 | alignment check | any data refrence in memory.3 | Fault | Y(0) |
18 | machine check | error codes and source are model dependent.4 | Fault | 無 |
19 | SIMD floating-point exception | SIMD floating-point instruction5 | Fault | 無 |
20-31 | reserved | |||
32-255 | maskable interrupts | External Interrupt from INTR pin or INT n instruction | Interrupt | 無 |
五、中斷描述符表 IDT
我們先來回顧一下上一講 【自制作業系統07】深入淺出特權級 說的四種門描述符
門 | type值 | 存在位置 | 用法 |
---|---|---|---|
任務門 | 0101 | GDT、LDT、IDT | 與TSS配合實現任務切換,不過大多數作業系統都不這麼玩 |
中斷門 | 1110 | IDT | 進入中斷後遮蔽中斷(eflags的IF位置0),linux利用此實現系統呼叫,int 0x80 |
陷阱門 | 1111 | IDT | 進入中斷後不遮蔽中斷 |
呼叫門 | 1100 | GDT、LDT | 使用者用call或jmp指令從使用者程序進入0特權級 |
你看,正如上一講所說,中斷門進入後先是遮蔽了中斷,也就是中斷例程的 上半部,程式中可以隨時開啟中斷,也就自然到了 下半部,這就是 linux 系統的處理方式。
如何找到中斷描述符表呢?你猜的沒錯,正如找 段描述符表,頁表 等一樣,有個 IDTR 暫存器儲存它的位置(0-15位是表界限,16-47位表示表基址),有個 lidt 指令負責載入 IDTR。經典做法,我們見過太多次了,就不多說啦,不理解的可以從本系列開頭開始看喲。
六、中斷處理過程
上圖就表示了整個中斷處理的過程,不過還有幾處圖中沒有顯示
特權級檢查:CPL <= 門描述符DPL && CPL > 目的碼段DPL
棧的處理:將 CS、EIP、EFLAGS、SS、ESP 暫存器的值壓入中斷處理程式使用的棧
七、8259A晶片
我們之前說過,外部裝置發出中斷訊號,進入 CPU 的 INT 引腳上。但如果有多個外部裝置近乎同時傳送中斷訊號,CPU 先處理哪一個呢?未被處理的中斷訊號又記錄在哪裡呢?這時候就需要有個 中間的代理裝置 來負責這個事情。
這個代理裝置叫做 可程式設計中斷控制器 PIC,其中 8259A 晶片是最常見的一種,我們這裡把它的內部結構展示出來,由於是硬體相關,就不展開細說了,但由於之後要為其進行程式設計,所以大家先有個印象。
八、中斷程式碼實現
由於到此篇幅過長,且中斷程式碼的實現也是需要很大篇幅描述的,包括 可程式設計中斷控制器的初始化,IDT 的初始化,以及中斷例程程式碼的編寫,所以將放在下一章進行講解。
【自制作業系統09】中斷的程式碼實現
寫在最後:開源專案和課程規劃
如果你對自制一個作業系統感興趣,不妨跟隨這個系列課程看下去,甚至加入我們,一起來開發。
參考書籍
《作業系統真相還原》這本書真的贊!強烈推薦
專案開源
專案開源地址:https://gitee.com/sunym1993/flashos
當你看到該文章時,程式碼可能已經比文章中的又多寫了一些部分了。你可以通過提交記錄歷史來檢視歷史的程式碼,我會慢慢梳理提交歷史以及專案說明文件,爭取給每一課都準備一個可執行的程式碼。當然文章中的程式碼也是全的,採用複製貼上的方式也是完全可以的。
如果你有興趣加入這個自制作業系統的大軍,也可以在留言區留下您的聯絡方式,或者在 gitee 私信我您的聯絡方式。
課程規劃
本課程打算出系列課程,我寫到哪覺得可以寫成一篇文章了就寫出來分享給大家,最終會完成一個功能全面的作業系統,我覺得這是最好的學習作業系統的方式了。所以中間遇到的各種坎也會寫進去,如果你能持續跟進,跟著我一塊寫,必然會有很好的收貨。即使沒有,交個朋友也是好的哈哈。
目前的系列包括
- 【自制作業系統01】硬核講解計算機的啟動過程
- 【自制作業系統02】環境準備與啟動區實現
- 【自制作業系統03】讀取硬碟中的資料
- 【自制作業系統04】從真實模式到保護模式
- 【自制作業系統05】開啟記憶體分頁機制
- 【自制作業系統06】終於開始用 C 語言了,第一行核心程式碼!
- 【自制作業系統07】深入淺出特權級