1. 程式人生 > >【自制作業系統08】中斷

【自制作業系統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】深入淺出特權級