一.80386中斷介紹
中斷最早是為了協調並同步高速的CPU與相對低速的外部設備而提出的概念。所謂中斷,是指當前程序/任務的執行過程中由於某種隨時可能發生的外部請求,使得CPU中斷正在執行的程序/任務,並跳轉執行另一個例程(中斷處理程序)或者中斷服務任務中去(發生任務切換)。在服務處理完成后返回到之前被打斷的程序斷點處,繼續執行。
隨着計算機技術的發展,中斷的概念被進一步延伸。除了最早概念中包括的外設引起的外部事件中斷(硬中斷)外,還提出了內部事件中斷(軟中斷)的概念。
在80386中,中斷一詞主要指的是硬中斷,而內部引起的軟中斷則被稱為異常。
硬中斷
硬中斷分為兩種類型:可屏蔽中斷(Interrupt Require,INTR)和不可屏蔽中斷(NonMaskable Interrupt,NMI)。
相同點:都是CPU在處理完當前指令時,檢查到硬中斷請求時才開始進行服務的;同時,會在服務完成后,繼續回到斷點執行被打斷時的下一條指令。
不同點:對於可屏蔽中斷,CPU根據標志寄存器EFLAGS中的IF位(中斷使能位)來決定是否響應。IF為0,表示關中斷,CPU不響應任何可屏蔽中斷。IF為1,表示開中斷,CPU會在每條指令執行完畢后檢查是否存在可屏蔽中斷的請求,如果存在,則進行中斷服務處理。而不可屏蔽中斷,顧名思義,無論標志寄存器中的IF位是多少,CPU都必須對此種中斷做出響應處理。
不可屏蔽中斷是通過CPU的NMI引腳產生的,中斷向量固定為2。可屏蔽中斷是通過CPU的INTR引腳產生的,中斷向量由引發中斷的硬件提供。
絕大多數的外設中斷請求都是可屏蔽中斷,少數特別嚴重的問題發生時,會產生不可屏蔽中斷。
軟中斷(異常)
80386中的軟中斷主要分為三種類型:失效、自陷和終止,軟中斷也被稱為異常。這三種異常的不同之處主要在於兩個方面,一是發生時的報告方式不同,二是異常中斷服務程序返回時的處理方式不同。
失效異常
失效異常在某一指令加載,啟動后、執行前被檢測到,並且在異常中斷服務程序執行完並返回后,重新執行該指令。失效異常也被成為故障(Fault),故障屬於通過某種確切手段能排除的異常。
在后面會介紹的頁式虛擬存儲的實現中,當處理器執行一條指令,訪問了不在物理內存中的內存頁時,便會引起失效中斷異常(缺頁異常),隨后CPU會跳轉執行操作系統提供的缺頁中斷處理程序,將所缺失的內存頁置換入物理內存中。隨后,CPU重新執行那條引起失效的指令,此時由於物理內存頁中已經存在了正確的數據,因此能正常的進行訪問。
自陷異常
自陷異常的處理比較像硬中斷。和失效異常不同的是,自陷是在引起自陷的指令執行完畢之后才產生的,同時在異常中斷服務程序執行完畢后順序執行引起自陷異常的下一條指令執行,而不是重新執行引起自陷的指令。
斷點debug調試便是使用自陷異常來實現的。在每個需要斷點的地方嵌入對應的自陷異常指令(例如INT 3),CPU執行到對應自陷異常指令時,便會跳轉到對應的異常處理程序中。這一異常處理程序通常是由操作系統提供的,操作系統可以找到注冊了斷點事件的調試處理程序,此時的調試器便可以展示被調試程序的當前內存等信息。斷點放過去之后,自陷異常處理程序便會返回,被調試的程序也能接着斷點處繼續執行了。
終止異常
終止異常是一種很嚴重的異常,對引起的異常無法確定確切位置,因而也沒有相應的處理辦法,只能終止並重啟系統。
硬件錯誤或者系統表中的錯誤數值等造成的異常便屬於終止異常。當發生終止異常時,原來運行的程序已經無法繼續執行下去,只能被迫終止。對於應用程序引起的終止異常,一般由操作系統將其終止;而操作系統內核本身引發的終止異常通常只能重啟系統,重新構建GDT等系統表。
二.中斷描述符表
不同類型的中斷請求都會擁有一個獨有的中斷類型碼。當中斷發生時,根據不同的中斷來源,或是由CPU提供一個中斷類型碼(例如內存缺頁異常),或是由指令給出(例如int n中的參數n),或是從外部的中斷控制器接收一個中斷類型碼。
在8086中,存在着一張中斷向量表,建立了中斷類型碼與對應中斷服務程序入口地址的關聯關系。而在80386中,由於引入了特權級保護,不能直接將服務程序的入口地址與中斷類型碼關聯,而是與一種被稱為中斷描述符的數據結構關聯,中斷描述符有着DPL等信息,訪問時受到特權級的限制。為了便於統一的進行管理,所有的中斷描述符數據需要按照對應的中斷類型碼順序集中存放在一起形成一張表,這張表被稱為中斷描述符表(Interrupt Descriptor Table,IDT)。
80386支持的中斷類型和8086一樣,都是256種。而中斷描述符指的是包括中斷門,陷阱們和任務門在內的門描述符,門描述符的大小和段描述符一樣都是64位,即8字節,因此中斷描述符表的實際邏輯大小最大為2KB。
中斷類型碼對應的異常信息:
當中斷發生時,CPU用中斷向量乘以8(因為中斷描述符是8字節的)去中斷描述符表中找到對應的中斷處理門描述符,進行相應的中斷處理。
和追蹤GDT的方式一樣,80386也提供了一個專用的寄存器用於定位中斷描述符表,被稱為中斷描述符表寄存器(Interrupt Descriptor Table Register,IDTR)。IDT和GDT一樣,全局唯一,因此IDTR被設計為48位,其中從高位到低位存放的分別是IDT的32位線性基地址和16位的界限。由於IDTR的存在,80386允許在進入保護模式后重新定義中斷描述符表,而不必像8086那樣將中斷向量表固定在低位內存空間。
門描述符(Gate Descriptors)
其實在之前的博客中已經或多或少提到了門描述符,如特權級保護中的實現系統調用而接觸到的調用門,任務切換中提到的任務門。而在中斷相關的內容中又涉及到了中斷門和陷阱門。在這里對門描述符進行統一的介紹。
段描述符是對某一指定范圍的內存段的描述(起始地址、段界限以及特權級),而門描述符則是針對程序控制轉移而提出的,指向的都是代碼段。
門描述符一共有四種,即調用門、任務門、中斷門和陷阱門。
調用門
調用門可以使用call far或者jmp far指令進行控制轉移。通過調用門進行的程序控制轉移,可以改變當前特權級CPL。
因此可以令低當前特權級CPL的程序控制轉移至高DPL的代碼段執行,操作系統提供的系統調用便是通過調用門實現的。調用門提供了必要的特權級保護,比起直接的控制轉移而提升當前特權級CPL,要安全很多。
如圖所示,調用門中存放着被調用過程的16位代碼段選擇子和32位的段內偏移量。
使用call far指令使用調用門時,可能會導致當前特權級CPL的變化,需要進行不同特權級堆棧的切換。由CPU固件進行調用時參數的壓棧處理,因此需要提供調用門所需要的參數個數,指導CPU進行堆棧參數的復制。調用門高32位中的低5位便是用於指定調用門所需參數個數的,5位代表着參數個數最多不能超過31(參數個數范圍:0~31)。
調用門描述符中的P位是有效位,一般應該為1表示有效。當調用P位為0的調用門時,CPU會發生一個失效異常,操作系統提供的中斷處理程序便可以依此對該調用門的使用次數加1,同時將其P位置為1。發生失效異常的CPU會在中斷處理程序返回后繼續嘗試執行該調用,此時P位校驗通過。而操作系統可以在調用門的例程中,將當前調用門的P位再次置為0,以重復上述過程,進行該調用門使用頻率的統計。
P位和DPL字段是四種門描述符共有的屬性,作用大致相同。
調用門描述符中的TYPE字段(高32位中的8-10位)固定為100,代表調用門。調用門只能存在於GDT、LDT中,不能存在於IDT中。
任務門
任務門主要用於進行任務的切換,因此其中的內容便是對應任務的TSS段選擇子,結構比較簡單。任務門的門描述符TYPE字段固定為101,代表任務門。
任務門既能在GDT、LDT中,也能存放於IDT中。因為除了正常的任務調度外,中斷發生時也可以觸發中斷處理任務的調度,此時中斷向量所指向中斷描述符表中的描述符便是任務門描述符。
中斷門和陷阱門
中斷門和陷阱門兩者的差別很小,一起進行介紹。
主要相同點:
1.中斷門和陷阱門都用於中斷處理,標識對應的中斷處理程序入口。因此都包含了中斷處理程序的16位代碼段選擇子和32位的段內偏移地址。
2.中斷門和陷阱門只能存放於中斷描述符表IDT中,而不能存放在GDT、LDT中。
主要不同點:
1.中斷門的TYPE字段固定為1110,而陷阱門的TYPE字段固定為1111(示意圖中的D在32位模式下為1,在16位模式下為0)。
2.通過中斷門進入中斷服務程序時,CPU會自動的關中斷(將EFLAGS的IF位置0),這意味着中斷門實現的中斷服務程序默認不支持中斷嵌套(有需要可以在程序中主動的開中斷)。而通過陷阱門進入中斷服務程序時,CPU不會復位IF的值,而是保持不變,這意味着陷阱門默認是支持進行中斷嵌套的。
中斷錯誤代碼
有些異常產生時,CPU會在中斷處理程序或中斷任務的棧中壓入一個中斷錯誤代碼。通常,這意味着異常和特定的段選擇子或中斷向量有關。錯誤代碼是32位,但高16位在80386中沒有使用,作為了保留位。
EXT位:
EXT位標識異常是否是由外部硬中斷產生的。EXT位為1時,表示當前異常是由外部硬中斷引發的(NMI或者INTR)。
IDT位:
IDT位標識段選擇子索引指向的是否是中斷描述符表IDT。IDT位為1時,表示段選擇子的索引部分是指向IDT的;IDT位為0時,表示段選擇子的索引部分是指向GDT或是LDT的。
TI位:
當IDT位為0時,需要進一步區分段選擇子的索引是指向GDT或是LDT。
當IDT=0,TI=0時,表示段選擇子索引指向的是GDT;當IDT=0,TI=1時,表示段選擇子索引指向的是LDT。
段選擇子索引:
段選擇子索引部分用於指向對應的描述符表中的某一個具體的段描述符。錯誤代碼中的段選擇子索引共13位,對應着訪問段描述符的16位段選擇子的高13位。
三.中斷優先級與中斷嵌套
為了能讓CPU及時的響應處理各種類型的中斷,根據引起中斷事件的重要性和緊迫程度,需要將中斷源分為不同的級別,稱作中斷優先級。
當低中斷優先級的中斷處理程序正在執行時,此時如果出現了更高中斷優先級的中斷請求,CPU需要能夠中斷當前低中斷優先級的處理程序,轉而去處理高中斷優先級的中斷請求,同時屏蔽掉低優先級的中斷請求,形成中斷嵌套。在80386中,允許多優先級層次的中斷嵌套。
80386的中斷優先級為上述不同類型的中斷設置了不同的中斷優先級。中斷優先級從高到低的排列分別是:終止異常 > 失效異常 > 自陷異常 > NMI不可屏蔽中斷 > INTR可屏蔽中斷。
80386CPU雖然對不同大類的中斷類型設置了不同的中斷優先級,可現實中的需求往往更復雜。例如,用戶通常希望自己使用鍵盤外設輸入的數據后能夠盡快的得到響應,而不是被大數據的磁盤外設IO給阻塞。但鍵盤和磁盤同為外部設備,都是使用INTR可屏蔽中斷與CPU進行交互的,其中斷優先級在CPU看來是相同的。因此,上述提到的80386中斷優先級機制無法實現令正在進行大數據磁盤IO的系統及時的響應時間上相對后操作的用戶鍵盤輸入。
為此,80386提供了對於INTR可屏蔽中斷處理的拓展。80386支持使用可編程中斷控制器8259A芯片將CPU的一根INTR可屏蔽中斷線擴展為8根以上的硬件中斷線,以支持更細致的INTR中斷優先級控制。
8259A芯片是可編程的,可以按照不同類型碼的中斷向量,為約定的不同外設的不同可屏蔽中斷向量賦予不同的中斷優先級,允許8259A芯片中的高優先級中斷打斷低優先級中斷的服務。使用了8259A芯片作為可屏蔽中斷的控制器后,CPU在處理可屏蔽中斷時,會根據8259A芯片是否發出了INTR請求來決定可屏蔽中斷的嵌套執行。
以上述鍵盤和磁盤的I/O優先級的需求問題舉例:操作系統在加載啟動時,會執行針對8259A芯片的編碼程序,對操作系統支持的各種INTR外設中斷進行統一編排,以決定不同的外設、中斷向量對應的中斷優先級。其中,便可以將鍵盤I/O在8259A芯片的中斷優先級設置為高於磁盤I/O的中斷優先級。這樣一來,當磁盤I/O中斷服務程序執行時,8259A芯片接收到了來自用戶鍵盤的輸入,根據當前編碼的中斷優先級,發現鍵盤I/O的優先級是高於此時正在處理的磁盤I/O中斷的。此時8259A芯片會向CPU發出一個鍵盤I/O的中斷請求,CPU收到來自8259A的中斷這一中斷請求后,便會暫時打斷磁盤I/O的處理,轉而嵌套中斷執行用戶鍵盤的I/O。用戶的操作得到了及時的相應,體驗得到了很大提升。而如果新出現的外設中斷沒有高於當前處理的INTR中斷,8259A只會將這一中斷暫時保存下來,而不通知CPU。
關於8259A芯片的工作原理以及編程方法,限於篇幅就不在這里展開了。
四.中斷服務處理流程
前面介紹了中斷處理的各個細節部分,下面總體的介紹一下80386的中斷處理流程。
從整體上看,80386的中斷處理全流程可以分為判斷是否存在中斷請求,如存在中斷則進行中斷服務處理,中斷服務處理返回后恢復現場這三部分。
判斷是否存在中斷請求
在當前指令即將結束的前一個機器周期時,CPU會按照上述中斷優先級先后順序依次判斷是否存在對應的中斷請求。
1、先判斷是否存在軟中斷異常,如果存在則進入中斷服務處理,中斷類型碼由異常的類型決定;
2、如果不存在軟中斷異常,接着判斷是否存在NMI不可屏蔽中斷,如果存在則進入中斷服務處理,中斷類型碼固定為2;
3、如果不存在NMI不可屏蔽中斷,接着判斷是否存在INTR可屏蔽中斷請求,如果存在INTR且IF=1開中斷,則進入中斷服務處理,中斷類型碼由發起INTR的外設給出;
4、如果不存在INTR可屏蔽中斷,最后判斷當前是否開啟了單步調試(標志寄存器EFLAGS的TF是否為1),如果TF=1,則進入中斷服務處理,中斷類型碼固定為1;
5、經過上述判斷后,CPU認為當前沒有中斷請求,將正常執行下一條指令。
進行中斷服務處理
當檢測到中斷請求后,CPU將會有一連串的硬件操作,跳轉中斷服務。
1、CPU從中斷請求信號中獲取對應的中斷類型碼(中斷向量)。
2、根據中斷向量從中斷描述符表中查找對應的門描述符,根據門描述符的類型進行中斷服務的處理(任務門進行任務切換,中斷門和陷阱門則進行中斷服務例程的調用)。
3、保護現場,按照門描述符的規則處理。如果是中斷門或陷阱門,先將當前的EFLAGS標志寄存器入棧,並設置IF、TF的值(陷阱門的IF不變,TF置0,而中斷門需要IF、TF都置0),最后按照順序壓入當前的CS、再壓入當前的EIP,以便中斷服務例程返回后恢復現場。如果是任務門,則進行之前博客中所說的任務切換,保存當前任務的TSS快照,並加載任務門中的中斷服務任務的TSS。
4、如果對應的中斷描述符是中斷門或陷阱門,跳轉進入對應的中斷服務例程;如果對應的中斷描述符是任務門,進行中斷服務任務的調度,掛起當前任務,令中斷服務任務獲得CPU控制權。
中斷服務返回
無論是中斷服務例程或是另一個中斷服務任務,正常情況下處理完中斷服務后都需要返回並恢復現場。
中斷門或陷阱門中斷服務例程的返回,按照之前入棧的相反順序將EIP、CS先后出棧;再將EFLAGS標志寄存器出棧,恢復現場(IRET指令)。
任務門中斷任務的返回,同樣是通過IRET指令進行的。在任務切換中提到的,執行中斷服務任務代碼中的IRET指令時,如果EFLAGS標志寄存器的NT嵌套任務位為1時,則會觸發任務切換。CPU通過當前TSS中的前一個任務TSS字段,將被打斷的任務TSS恢復,達到中斷服務任務返回,恢復現場的目的。
五.總結
中斷機制是CPU工作原理中很重要的一環,也是現代操作系統的基礎。80386的中斷由於保護模式和多任務切換的原因,原理比8086要復雜不少。
但只有理解了80386的中斷,才能在學習基於80386的操作系統時,更好理解的系統、硬件的工作機制,學習和閱讀源碼時也能更加順利。更進一步的,許多應用層的I/O、同步/異步程序的底層實現原理,也多少與硬件的中斷功能有關聯,如果站在CPU硬件這一層面去看上層的應用技術,也許能有更深刻的理解。