8086中斷介紹
任何一種CPU,都具備一種能力,可以在執行完當前正在執行的指令之后,檢測到來自CPU內部或外部產生的特殊通知信息,並立即對所接收到的信息做出相應的處理。這類特殊的信息,被稱作中斷信息。
顧名思義,中斷指的是CPU不去正常執行接下來的指令,而是被中斷,轉而處理中斷信息。
中斷信息的種類有很多,但卻有着一些共同點,中斷信息中都包含了中斷信息的類型碼,用於標識中斷信息。8086的中斷類型碼是8位的,這代表着8086CPU最多可以處理256種不同的中斷信息。
中斷處理程序
CPU接受到了中斷信息后,需要進行相應的處理,處理邏輯依然是由開發人員編寫程序來控制的,所編寫的程序被稱作中斷處理程序。一般來說,需要編寫不同的中斷處理程序以應對不同的中斷信息。
要令CPU中止當前指令的執行,轉而跳轉執行中斷處理程序,其原理依然是通過改變8086CPU中CS:IP的值,使之指向中斷信息對應的中斷處理程序。
想要CPU令處理不同的中斷信息時跳轉到對應的中斷處理程序,則必須要有一種機制將中斷信息和中斷處理程序建立關聯。
中斷向量表
8086CPU的設計者提供了一種叫做中斷向量表的結構,用於建立中斷類型碼和中斷處理程序入口的關聯關系。中斷向量表,就是中斷程序入口地址的一個列表,被保存在指定的內存地址中,便於CPU讀取。
每一個中斷向量列表項(即中斷處理程序入口地址)是32位的,占兩個字的空間,其中高16位存放段地址,低16為存放偏移地址。中斷向量表在8086CPU中的位置是固定的,位於0000:0000~0000:03ff這一特殊內存空間中(CPU會固定的到約定的內存處獲取數據)。
CPU在跳轉中斷處理程序時,以中斷類型碼*4+2字單元中的數據設置CS,中斷類型碼*4字單元中的數據設置IP,如此一來,便能正確的跳轉對應的中斷處理程序。
中斷處理過程
雖然已經說明了CPU是如何根據中斷信息中的類型碼跳轉執行指定的中斷處理程序。但實際上,在CPU執行完中斷處理程序后,還需要能夠返回中斷處理之前的程序,並接着執行之前的程序。
因此,CPU在跳轉中斷處理程序前,需要和跳轉子程序時進行同樣的操作,將當前的CS:IP壓入棧中,以便中斷處理程序返回時,恢復現場。
中斷過程大致有以下步驟:
1.從中斷信息中取得中斷類型碼
2.pushf 標志寄存器flag入棧(第三步中斷過程會修改flag的值,因此需要保護當前程序下flag的內容,便於返回后還原)
3.將標志寄存器中關於中斷的TF、IF設置為0(TF=0,IF=0)
4.CS入棧(push CS)
5.IP入棧(push IP)
6.以中斷類型碼*4+2的字單元中的數據設置CS,中斷類型碼*4的字單元中的數據設置IP(CS=N*4+2,IP=N*4)
7.執行編寫好的中斷處理程序
8.IP出棧
9.CS出棧
10.popf 標志寄存器flag出棧
縱觀整個中斷處理過程,8086CPU的中斷跳轉指令將1/2/3/4/5/6整合為了一個由硬件自動執行的完整操作,用於跳轉至中斷處理程序,開發人員無法直接干預這一過程。而對於8/9/10三個步驟,8086匯編的設計者則提供了指令iret(interrupt return中斷返回)指令,將中斷返回時必須的出棧操作合而為一,簡化了中斷返回時的步驟,降低了出錯的可能性。
要想使用8086CPU提供的中斷處理功能,有幾點需要注意:
1.將中斷類型碼對應的中斷處理程序入口地址存放安裝在中斷向量表中正確的位置。
2.中斷處理程序和編寫子程序類似,需要將用到的寄存器預先壓入棧中保存,並在返回前按順序彈出恢復,避免寄存器沖突。
3.中斷處理程序返回時,需要和中斷跳轉時的相關寄存器壓棧順序相反,將IP、CS、Flag按順序出棧(使用iret指令),以跳轉回中斷處理前的程序,恢復現場。
中斷向量表初始化
中斷向量表的建立和初始化工作是由 BIOS 在計算機啟動時負責完成的。BIOS 為每個中斷號填寫入口地址,因為它不知道多數中斷處理程序的位置,所以,一律它們指向一個相同的程序入口地址,在那里,只有一條指令:iret。
也就是說,當這些中斷發生時,只做一件事,就是立即返回。當計算機啟動后,操作系統和用戶程序再根據自己的需要,來修改某些中斷的入口地址,使它指向自己的程序代碼。
中斷來源類型
中斷信息從來源上可以分為內中斷和外中斷兩種。
內中斷
內中斷,便是指來自CPU內部的中斷信息。
8086的內中斷大致可以分為以下四種:
1.除法錯誤(例如除數為0),中斷類型碼為0
2.單步執行,中斷類型碼為1
3.執行into指令,中斷類型碼為4
4.執行int指令,指令的格式為int n(byte類型 idata立即數),n表示中斷類型碼。例如在DOS下,用於返回DOS操作系統的int 21h。
內中斷的存在允許開發人員在程序中主動地引發中斷,控制CPU執行中斷處理程序。
外中斷
CPU除了能夠執行程序指令外,還能對所連接的外設實施控制,接收外設的輸入數據、向外設輸出數據(I/O)。
以CPU獲取外設的輸入為例分析,CPU應該如何處理外設輸入事件呢?鍵盤、鼠標或是觸屏等用戶操作;磁盤、網卡的輸入都是隨時隨地可能發生的,CPU需要及時的獲取到這一消息,並做出相應的處理。
從CPU到外設這數據接受和發送的雙方看來,對是否有數據需要讀取的檢測方式無非是推與拉兩種。CPU可以不斷的對所有的外設端口進行輪詢,判斷是否存在數據的輸入,並進行處理(CPU主動拉取數據)。另一方面,外設也可以在有數據需要CPU處理時,主動的推送通知給CPU,CPU收到通知后再來讀取對應外設的數據,可以避免大量無效的輪詢(外設主動推送通知)。
由於CPU連接的外設可能會很多,且大多數外設在一次輪詢周期內可能並沒有數據需要處理。周期性的輪詢會一定程度上占用CPU資源,降低CPU的執行效率。因而在CPU硬件層面,由外設推送數據的方案效率更高,更有效。
8086CPU提供了外中斷這一機制,允許外設以外中斷的形式通知CPU,與之交互。外中斷可以分為兩大類:可屏蔽中斷、不可屏蔽中斷。
可屏蔽中斷
可屏蔽中斷指的是CPU可以選擇不響應的中斷。8086CPU是否響應中斷,是根據當前標志寄存器中IF的值判斷,若IF=1,CPU在處理當前指令后立即對中斷指令做出響應;若IF=0,則CPU不響應可屏蔽中斷。
幾乎所有的外設引發的外中斷,都是可屏蔽中斷。典型的可屏蔽中斷源的例子是打印機中斷,CPU對打印機中斷請求的響應可以快一些,也可以慢一些。在被屏蔽中斷期間,打印機會反復的發送中斷信號,這期間讓打印機等待一會是可以接受的。
不可屏蔽中斷
不可屏蔽中斷指的是CPU必須做出響應的中斷。無論IF的值是多少,都必須在收到不可屏蔽中斷后,立即做出相應。在8086CPU中,不可屏蔽中斷的中斷類型碼固定為2。
典型的非屏蔽中斷源的例子是CPU電源斷電,一旦出現,必須立即無條件地響應。CPU電源掉電時真的會引發中斷嗎?
為什么進行中斷處理時,需要先將IF、TF都設置為0?
將IF設置為0的原因主要是因為8086CPU的設計者認為中斷處理程序一般是不需要對其它中斷做出響應的。
因此默認的將IF設置為0,禁止CPU處理可屏蔽中斷。當然,如果有的中斷處理程序確實需要處理可屏蔽中斷,也可以在中斷處理程序中開中斷,將IF重新設置為1。指令sti,設置IF=1;指令cti,設置IF=0。
將TF單步調試功能關閉的原因則是為了避免出現單步中斷的死循環。
試想如果開啟了單步調試功能,那么在進入中斷處理程序,並執行完第一條指令后,便會引發單步中斷,進而跳轉到單步中斷處理程序中。而在執行了單步中斷處理程序的第一條指令后,又會再度引發單步中斷,這成為了一個死循環。因此,在進入中斷處理程序之前,必須首先把TF置為0,關閉單步調試功能,以避免上述情況產生。
總結
學習8086匯編的主要目的是為了更好的學習操作系統,而中斷這一概念在操作系統中是十分重要的,操作系統中許多功能都依賴硬件所提供的中斷能力。
例如,通過8086中斷的學習,我想通了一個困擾我很久的問題:在單核CPU機器上,操作系統是如何從當前正在運行的應用程序手中奪回控制權,進行應用進程調度的?雖然之前了解過時鍾中斷這一概念,但是卻一直沒有徹底
的將這些知識邏輯自洽的串聯起來(好菜啊>_<)。
我目前給出的回答是:這依賴於時鍾外設周期性發出的時鍾中斷信號。當CPU正在運行應用程序時,時鍾中斷會使得CPU停止執行當前應用程序的指令,轉而去執行時鍾中斷處理程序。而時鍾中斷處理程序由操作系統提供,因此此
時CPU的控制權便又回到了操作系統程序手中。操作系統程序便可以按照一定的規則,對運行中並發的應用程序進行相應的調度。通過時鍾中斷這一機制,操作系統得以始終擁有CPU的主導權。
盡管不同CPU硬件的結構不同,實現中斷處理的細節也不盡相同,但一些核心的概念是不會發生太大變化的。理解了較為簡單的8086CPU中斷,能為理解更復雜的硬件及操作系統層面的中斷機制打下一個好的基礎。