中斷,英文名為Interrupt,計算機的世界裡處處都有中斷,任何工作都離不開中斷,可以說整個計算機系統就是由中斷來驅動的。那麼什麼是中斷?簡單來說就是

CPU停下當前的工作任務,去處理其他事情,處理完後回來繼續執行剛才的任務

,這一過程便是中斷。

本文旨在進一步揭開中斷機制的面紗,理清中斷的過程,就中斷做出以下幾個方面的介紹:中斷分類,中斷描述符表,中斷控制器,和中斷過程。詳細的思維導圖如下:

一文講透計算機的“中斷”

中斷分類

一文講透計算機的“中斷”

1

外部中斷

1、可遮蔽中斷

透過INTR線向CPU請求的中斷

,主要來自外部裝置如硬碟,印表機,網絡卡等。此類中斷並不會影響系統執行,可隨時處理,甚至不處理,所以名為可遮蔽中斷。

2、不可遮蔽中斷

透過NMI線向CPU請求的中斷

,如電源掉電,硬體線路故障等。這裡不可遮蔽的意思不是不可以遮蔽,不建議遮蔽,而是問題太大,遮蔽不了,不能遮蔽的意思。

注:INTR和NMI都是CPU的引腳

2

內部中斷(軟中斷,異常)

1、陷阱:是一種有意的,預先安排的異常事件

,一般是在編寫程式時故意設下的陷阱指令,而後執行到陷阱指令後,CPU將會呼叫特定程式進行相應的處理,

處理結束後返回到陷阱指令的下一條指令

。如系統呼叫,程式除錯功能等。

儘管我們平時寫程式時似乎並沒有設下陷阱,那是因為平常所用的高階語言對底層的指令進行了太多層的抽象封裝,已看不到底層的實現,但其實是存在的。例如

printf函式,最底層的實現中會有一條int 0x80指令

,這就是一條陷阱指令,使用0x80號中斷進行系統呼叫。

2、故障:故障是在引起故障的指令被執行,但還沒有執行結束時,CPU檢測到的一類的意外事件。

出錯時交由故障處理程式處理,

如果能處理修正這個錯誤,就將控制返回到引起故障的指令即CPU重新執這條指令。如果不能處理就報錯

常見的故障為缺頁,當CPU引用的虛擬地址對應的物理頁不存在時就會發生故障。缺頁異常是能夠修正的,有著專門的缺頁處理程式,它會將缺失的物理頁從磁碟中重新調進主存。而後再次執行引起故障的指令時便能夠順利執行了。

3、終止:執行指令的過程中發生了致命錯誤,不可修復,程式無法繼續執行,只能終止,通常會是一些硬體的錯誤。

終止處理程式不會將控制返回給原程式,而是直接終止原程式

中斷描述符表

中斷描述符表類似全域性描述附表,表記憶體放的描述符,與GDT不同的是IDT內可以存放4種描述符:任務門描述符,陷阱門描述符,呼叫門描述符,中斷門描述符。

咱們在此只介紹中斷門描述符,4種描述符除了任務門其他都類似,中斷門也是最常用的,如Linux的系統呼叫就是使用中斷門實現的。

1

中斷描述符

一文講透計算機的“中斷”

中斷描述符的結構如上,重要欄位和屬性為已標出,有個瞭解就好,不必深究各個位的具體含義。

2

中斷向量號

在介紹中斷向量號之前,我們先引入一個段選擇子(segment selector)的概念。所謂段選擇子,就是段暫存器的值,段選擇子的高13位為全域性描述符表的索引號,其他的位置是屬性位,這就好比是陣列下標索引陣列元素。

至於中斷向量號,作用等同於段選擇子的高13位,用來在IDT中索引相對的中斷描述符,但沒有相應的類似段選擇子的結構。

部分中斷向量表,需要了解的部分如下圖所示:

一文講透計算機的“中斷”

3

中斷描述符表暫存器IDTR

一文講透計算機的“中斷”

IDTR也類似於GDTR,存放的是48位資料資訊,高32位是IDT的地址,低16位表示IDT的界限。

同GDTR,IDTR也有相應的載入指令:

lidt m16&32

,m是那48位資料資訊,lidt指令將其載入到IDTR暫存器,使得CPU知道IDT在哪。

中斷控制器

每個獨立執行的外設都可以是一箇中斷源,能夠向CPU傳送中斷請求,為了方便管理和減少引腳數目,設立了中斷控制器,讓所有的可遮蔽中斷都透過INTR訊號線與CPU進行交流。

中斷控制器中較為流行的是Intel 8259A晶片,下面對8259A作簡單介紹:

1

級聯

單個8259A晶片只有8根中斷請求訊號線

(IRQ0, IRQ1, … , IRQ7),這是不夠用的,所以採用多個8259A晶片。將它們如下圖一樣像串聯的方式組合起來,這種組合方式就叫做級聯。

一文講透計算機的“中斷”

級聯時只能有一個主片,其餘的均為從片

,最多可級聯9個,即最多支援64箇中斷。為什麼不是8 * 9 = 72個呢?從上圖可以看出

級聯時後面的晶片會佔用前面晶片的一個IRQ介面

,而最後一個8259A沒有其他人佔用,所以8259A的個數和支援的中斷數關係為7n + 1。

2

8259A的一些功暫存器和功能部件

1、IMR:Interrupt Mask Register,中斷遮蔽暫存器,其中的每個位標誌著一個外設,1表示遮蔽該外設,0表示中斷允許。

2、IRR:Interrrupt Request Register,中斷請求暫存器,請求中斷的外設在IRR對應的位 值為1。當有多箇中斷請求時,IRR暫存器中多位將會置1,相當於維持了一個請求中斷的佇列。

3、ISR:In_Service Register,中斷服務暫存器,正在進行處理的中斷在ISR對應的位值為1。

4、PR:Priority Resolver,優先順序裁決器,用於從IRR中挑選一個優先順序最大的中斷。(IRQ介面號小的優先順序大)。

中斷過程

一文講透計算機的“中斷”

1

中斷請求

1、

當外設發出中斷訊號後,訊號被送入8259A;

2、

8259A檢查IMR暫存器中是否遮蔽了來自該IRQ的訊號,若IMR暫存器中對應的位為1,表示遮蔽了IRQ代表的中斷,則丟掉此中斷訊號,若IMR暫存器中對應的位為0,表示未遮蔽此中斷,則將IRR暫存器中與此中斷對應的位 置1。

3、

PR優先順序裁決器從IRR暫存器中挑選一個優先順序最大的中斷,然後8259A向CPU傳送INTR訊號。

2

中斷響應

1、

CPU收到INTR訊號後便知道有新的中斷了,在執行完當前指令後,向8259A傳送一箇中斷回覆訊號。

2、

8259A收到回覆訊號後,將選出來的優先順序最大的中斷在ISR暫存器中相應的位 置1,表示該中斷正在處理,同時將此中斷在IRR暫存器中相應的位 置0,相當於將此中斷從中斷請求佇列中去掉。

3、

CPU再次向8259A傳送INTR訊號,表示想要獲取中斷向量號。

4、

8259A透過資料匯流排向CPU傳送中斷向量號,

中斷向量號 = 起始向量號 + IRQ介面號

,一般起始向量號為32,從中斷向量表可看出0—31已經被佔用,後面的32—127是分配給可遮蔽中斷的,所以此處外設的中斷設定的起始向量號便為32。

3

保護現場——壓棧

1、

CPU據中斷向量號去IDT中獲取中斷描述符,取出選擇子中的DPL與當前特權級CPL進行比較,若特權級發生變化,則需要切換棧。(不同特權級有著不同的棧,如Linux使用了0, 3特權級,則有兩個棧,一個核心棧,一個使用者棧)

2、

於是處理器臨時儲存當前的舊棧SS和ESP的值,從TSS(每一個任務有一個TSS結構,其中儲存著不同特權級棧的SS和ESP值)中獲取與DPL特權級同的棧資訊載入到SS和ESP暫存器。再將舊棧SS和ESP的值壓入新棧中。若沒有特權級變化,則跳過此步驟。

一文講透計算機的“中斷”

3、壓入程式狀態資訊,即EFLAGS暫存器

一文講透計算機的“中斷”

4、壓入斷點,即返回地址,即當前任務的CS,EIP值。

一文講透計算機的“中斷”

5、若該中斷有錯誤碼,壓入錯誤碼

一文講透計算機的“中斷”

4

定位中斷服務程式

直接先上流程圖:

一文講透計算機的“中斷”

具體步驟如下:

1、

據中斷向量號去IDT中索引中斷描述符,具體操作:取出IDTR中的IDT地址,加上中斷向量號 * 8,得到的地址指向所要的中斷描述符。

2、

據中斷描述符中的段選擇子去GDT中索引段描述符,具體操作:取出GDTR中的GDT地址。加上段選擇子高13位 * 8, 得到的地址為中斷處理程式所在段的段基址。

3、

上一步得到的段基址加上段描述符中的段內偏移量得到的地址變為中斷服務程式的地址。

5

中斷處理過程

中斷的實際處理過程就是執行中斷處理程式,Linux將中斷處理程式分為上下兩部分,需要緊急處理立即執行的歸為上半部,不那麼緊急的歸為下半部。

這便涉及到了開關中斷的問題。開中斷,即EFLAGS的IF位置1,表示允許響應中斷;關中斷,即EFLAGS的IF位置0,表示不允許響應中斷。

1、

上半部分是刻不容緩的,需要立即執行的部分,所以要在關中斷的狀態下執行。

2、

而下半部分不那麼緊急,在開中斷的情況下進行,如果此時有新的中斷髮生,當前中斷處理程式便會換下CPU,CPU會另尋時間重新排程,完成整個中斷處理程式。

6

中斷返回——出棧

中斷返回就是出棧的過程,將第三步保護現場壓入棧中的資訊彈出。

1、

有錯誤碼彈出錯誤碼。

2、

此時的棧頂指標ESP應指向EIP_old,剩餘棧中的資訊使用iret指令彈出,CPU執行到iret指令時再次檢查和比較特權級是否變化。

3、

彈出EIP_old, CS_old

4、

若特權級變化,將ESP_old, SS_old, 載入到ESP,SS暫存器。

至此,中斷已返回,中斷也已處理。

上述的中斷過程是我根據資料照著自己的理解分為了6步,每步又有許多微操作,可能跟某些書籍資料等所劃分的步驟不同,甚至一些微操作的順序也不太一樣,比如說中斷處理時什麼時候關中斷,我查閱了許多資料和書籍,講述得都有區別。

不同作業系統在中斷方面的實現有所不同,但總體來說都會經歷上述的步驟,可能細微之處略有差別,卻也不影響我們瞭解中斷的過程。

END

中斷是作業系統重要的機制,沒有中斷,作業系統什麼也幹不了,沒法輸入沒法輸出,不能管理硬體資源,也不能向上層應用提供服務。而且作業系統本身就像是一個死迴圈,等待事件發生需求來臨,然後為其提供服務解決問題。而這事件的發生與處理就是靠中斷機制來控制的,所以說中斷對於作業系統來說有著舉足輕重的作用,而我們也有必要了解中斷,理清中斷的過程。

喜歡本文的朋友,歡迎關注公眾號

程式設計師小灰

,收看更多精彩內容