中斷異常

Linux內核學習筆記:中斷與異常
[日期:2012-03-31] 來源:Linux社區  作者:yaozhenguo2006

 
中斷分爲同步中斷與異步中斷。同步中斷也叫異常是CPU執行特定的指令產生的事件,他打斷CPU正常執行的指令而執行設定好的指令。異步中斷也叫中斷是由CPU外部中斷信號產生的,每個CPU都有一個或多箇中斷引腳,當引腳上出現中斷中斷信號的時候,CPU就會停止執行當前的指令而去執行特定的代碼。在linux中,中斷處理至關重要,它影響着整個系統的性能。中斷程序運行時,當前進程Current宏無效,所以中斷程序是一個單獨的內核控制路徑,不能夠進行進程切換。
 

相關閱讀:Linux內核學習筆記:中斷的下半部分 http://www.linuxidc.com/Linux/2012-03/57752.htm

因爲中斷的特殊性,所以中斷處理設計就必須考慮以下幾點:

(1)中斷處理時間儘可能短,這個可以通過一定的方法使得中斷分階段進行。

(2)中斷嵌套的允許,這樣極大縮短了中斷延遲

(3)關中斷的儘可能時間短,次數少。

x86系統中使用中斷描述符來描述各種中斷,中斷描述符表是個系統表,首地址存放在idtr寄存器中。中斷描述符中裝有段選擇符以及段內偏移量,這個來確定中斷處理程序的地址。

一. 內核初始化中斷描述符表

中斷描述符表必須在開中斷時初始化,初始化這個表,linux有兩個階段。第一個階段是初步初始化,在中斷描述符表中,插入相同的表項,中斷描述符表有256相,這個表項指向的中斷處理程序,只是保存一些寄存器,然後打印“Unknown interrupt”,然後就退出。第二階段,linux用有意義的中斷處理程序的地址填充表項。x86體系結構中256箇中斷向量分配如下:

0-19        非屏蔽中斷與異常

20-31       保留

32-127      外部中斷

128         系統調用異常

129-238     外部中斷

......

二. 中斷與異常的硬件處理

在中斷髮生時,CPU硬件首先要完成一定的工作,使得程序計數器能夠正確跳轉到中斷處理程序中。x86體系結構的中斷硬件處理如下:

(1) 通過讀取中斷控制器的IO端口,或得中斷號,並且由此中斷號在中斷描述符表中找到相應的中斷描述符。

(2)檢查中斷的權限。總體的原則是引起中斷的程序的特權級別CPL(當前特權級)必須高於低於中斷處理程序的特權。

(3)檢查是否發生特權級的變化,也就是檢查是在內核態發生的中斷,還是在用戶態發生的中斷。如果在用戶態發生的中斷,內核發生中斷CPU肯定切換到了內核態,所以特權級,肯定發生了變化。如果在內核態發生的中斷,特權級就沒有變。如果是由用戶態切換到內核態那麼,在ss和esp中還保留這用戶態堆棧的地址,所以得用內核態的地址填充。

(4)裝載cs,eip地址,值是在中斷描述符表中獲取的。

三. 同步中斷:異常的內核處理

CPU產生的異常,內核都當作錯誤處理。但是有一個異常是linux利用CPU高效管理硬件的前提,那就是缺頁異常,注意,同步中斷當前進程有效。異常處理程序一般分爲三部分:

(1) 在內核堆棧中保存大多數CPU寄存器的內容

這部分是用匯編程序編寫的,首先將C中斷處理函數的地址壓入堆棧,然後保存8個C語言可能用到的寄存器的值到內核棧中,把棧中esp+36處的硬件出錯碼拷貝到edx寄存器中,並在這個位置填-1(硬件出錯嗎是),然後把esp+32出的C中斷處理程序地址裝入edi,在這個位置寫入es的值。把棧頂地址保存在eax寄存器中。然後調用在edi中高級C函數

(2) 調用高級C語言處理異常

大部分的C異常處理程序都是把硬件出錯碼保存在當前進程的進程描述符中。然後給進程發送一個信號。異常處理程序還檢查發生異常是在用戶態或者內核態,如果是在內核態,那麼說明內核有BUG,調用die函數,使得系統產生oops,並殺死當前進程。

(3) 從異常返回


本篇文章來源於 Linux公社網站(www.linuxidc.com)  原文鏈接:http://www.linuxidc.com/Linux/2012-03/57751.htm

四. 異步中斷

異步中斷,當前進程是無效的。異步中斷分爲:IO中斷,時鐘中斷,處理器之間中斷。下面重點學習linux的IO中斷處理。所有中斷處理程序都執行四個相同的工作。

(1)在內核態保存IRQ的只和寄存器的值

(2)應答中斷

(3)執行這個IRQ的所有中斷服務例程。

(4)中斷返回

IRQ基本的數據結構爲irq_desc_t,每一個IRQ都有一個這樣的數據結構。這個數據結構包括:操作可編程中斷控制器的方法,中斷處理程序鏈表首地址,中斷向量的狀態,以及各種鎖。操作可編程中斷控制器的方法用來應答中斷,而action字段是中斷處理程序鏈表首地址,這個鏈表是irqaction數據結構的鏈表。這個數據結構包含有中斷處理程序地址。下面學習中斷髮生時,linux內核處理的過程。
 

(1)當CPU通過接收到一箇中斷的時候,首先硬件自動處理這個中斷,與異常一樣。

(2)然後跳轉到裝載在中斷描述符表中的通用中斷處理程序,interrupt[n]

這個函數,首先將n-256壓入堆棧,然後跳轉到common_interrupt函數中。在各函數首先保存CPU相關的寄存器,跳轉到do_IRQ函數中。這個函數是中斷處理的主函數。

(3)do_IRQ函數

這個函數應答中斷,然後依次執行irq_desc_t數組中相應的irqaction中的中斷處理函數。然後返回。

五. 從中斷和異常返回

從中斷中返回意味這要恢復被中斷的程序。被中斷的程序有可能是以下幾種:中斷處理程序,可延遲函數,普通進程(包括內核態與用戶態)。如果是前兩種內核控制路徑,在這兩種控制路徑執行時內核是禁止搶佔的。所以考慮的情況簡單。如果中斷的是普通進程或者內核線程。那麼在返回的時候必須考慮以下問題:

(1)掛起的進程切換請求

(2)掛起的信號

(3)進程跟蹤標誌

這些標誌都在進程thread_info結構中的flags中,所以返回代碼必須檢查這些標誌,執行不同的操作。返回代碼有兩個ret_from_intr和ret_from_exception,分別對應着中斷與異常。這兩個函數從本質上做了如下的操作:

首先將當前進程的thread_info描述符地址裝載到ebp寄存器中,判斷壓入棧中的cs和eflags的值,確定被中斷的是內核態或者是用戶態。如果是內核態執行resume_kernel,如果是用戶態執行resume_userspace

(1)恢復到內核態

首先檢查是否允許內核搶佔,從棧中恢復現場,恢復內核程序的執行。如果允許內核搶佔,檢查當前進程的調度標誌,如果需要調度那麼調用調度程序執行進程調度。

(2)恢復到用戶態

這個主要檢查是否有重新調度標誌,如果有調度。還有檢查信號與單步運行標誌,如果設置則執行相應的處理。如果所有標誌都沒設置,直接恢復原來進程的執行。


本篇文章來源於 Linux公社網站(www.linuxidc.com)  原文鏈接:http://www.linuxidc.com/Linux/2012-03/57751p2.htm

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章