參考 http://www.yesky.com/20010813/192117.shtml
結構化程序設計思想認為:程序 = 數據結構 + 算法。數據結構體現了整個系統的構架,所以數據結構通常都是代碼分析的很好的着手點,對Linux內核分析尤其如此。
比如,把進程控制塊結構分析清楚了,就對進程有了基本的把握;
再比如,把頁目錄結構和頁表結構弄懂了,兩級虛存映射和內存管理也就掌握得差不多了。
為了體現循序漸進的思想,在這我就以Linux對中斷機制的處理來介紹這種方法。
首先,必須指出的是:在此處,中斷指廣義的中斷概義,它指所有通過idt進行的控制轉移的機制和處理;它覆蓋以下幾個常用的概義:中斷、異常、可屏蔽中斷、不可屏蔽中斷、硬中斷、軟中斷 … … …
I、硬件提供的中斷機制和約定
一.中斷向量尋址:
硬件提供可供256個服務程序中斷進入的入口,即中斷向量;
中斷向量在保護模式下的實現機制是中斷描述符表idt,idt的位置由idtr確定,idtr是個48位的寄存器,高32位是idt的基址,低16位為idt的界限(通常為2k=256*8);
idt中包含256個中斷描述符,對應256個中斷向量;每個中斷描述符8位,其結構如圖一
二.異常處理機制:
Intel公司保留0-31號中斷向量用來處理異常事件:當產生一個異常時,處理機就會自動把控制轉移到相應的處理程序的入口,異常的處理程序由操作系統提供,中斷向量和異常事件對應如表一:
表一、中斷向量和異常事件對應表
中斷向量號 | 異常事件 | Linux的處理程序 |
0 | 除法錯誤 | Divide_error |
1 | 調試異常 | Debug |
2 | NMI中斷 | Nmi |
3 | 單字節,int 3 | Int3 |
4 | 溢出 | Overflow |
5 | 邊界監測中斷 | Bounds |
6 | 無效操作碼 | Invalid_op |
7 | 設備不可用 | Device_not_available |
8 | 雙重故障 | Double_fault |
9 | 協處理器段溢出 | Coprocessor_segment_overrun |
10 | 無效TSS | Incalid_tss |
11 | 缺段中斷 | Segment_not_present |
12 | 堆棧異常 | Stack_segment |
13 | 一般保護異常 | General_protection |
14 | 頁異常 | Page_fault |
15 | Spurious_interrupt_bug | |
16 | 協處理器出錯 | Coprocessor_error |
17 | 對齊檢查中斷 | Alignment_check |
中斷和異常的區別
中斷指廣義的中斷概義,它指所有通過idt進行的控制轉移的機制和處理(interrupt description table,中斷描述符表)
硬件提供可供256個服務程序中斷進入的入口,即中斷向量。Intel公司保留0-31號中斷向量用來處理異常事件。另有0x80,也就是128,是系統調用所用的中斷。
所以要說區別,那就是0-31號的中斷向量表示的是Intel公司定義的異常事件(包括常見的page fault、除0異常)等。
II、Linux的中斷處理
硬件中斷機制提供了256個入口,即idt中包含的256個中斷描述符(對應256個中斷向量)。
而0-31號中斷向量被intel公司保留用來處理異常事件,不能另作它用。對這0-31號中斷向量,操作系統只需提供異常的處理程序,當產生一個異常時,處理機就會自動把控制轉移到相應的處理程序的入口,運行相應的處理程序;而事實上,對於這32個處理異常的中斷向量,此版本(2.2.5)的Linux只提供了0-17號中斷向量的處理程序,其對應處理程序參見表一、中斷向量和異常事件對應表;也就是說,17-31號中斷向量是空着未用的。
既然0-31號中斷向量已被保留,那么,就是剩下32-255共224個中斷向量可用。這224個中斷向量又是怎么分配的呢?在此版本(2.2.5)的Linux中,除了0x80 (SYSCALL_VECTOR)用作系統調用總入口之外,其他都用在外部硬件中斷源上,其中包括可編程中斷控制器8259A的15個irq;事實上,當沒有定義CONFIG_X86_IO_APIC時,其他223(除0x80外)個中斷向量,只利用了從32號開始的15個,其它208個空着未用。
這些中斷服務程序入口的設置將在下面有詳細說明。
一.相關數據結構
- 中斷描述符表idt: 也就是中斷向量表,相當如一個數組,保存着各中斷服務例程的入口。(詳細描述參見圖一、中斷描述符格式)
- 與硬中斷相關數據結構:
我的理解:handler指的是哪個處理器來處理這個;action是實際的處理例程。action會交給handler處理。不知道對不對。
http://www.yesky.com/20010813/192117_2.shtml
三. Bottom_half處理機制
在此版本(2.2.5)的Linux中,中斷處理程序從概念上被分為上半部分(top half)和下半部分(bottom half);在中斷發生時上半部分的處理過程立即執行,但是下半部分(如果有的話)卻推遲執行。內核把上半部分和下半部分作為獨立的函數來處理,上半部分決定其相關的下半部分是否需要執行。必須立即執行的部分必須位於上半部分,而可以推遲的部分可能屬於下半部分。
那么為什么這樣划分成兩個部分呢?
- 一個原因是要把中斷的總延遲時間最小化。Linux內核定義了兩種類型的中斷,快速的和慢速的,這兩者之間的一個區別是慢速中斷自身還可以被中斷,而快速中斷則不能。因此,當處理快速中斷時,如果有其它中斷到達;不管是快速中斷還是慢速中斷,它們都必須等待。為了盡可能快地處理這些其它的中斷,內核就需要盡可能地將處理延遲到下半部分執行。
- 另外一個原因是,當內核執行上半部分時,正在服務的這個特殊IRQ將會被可編程中斷控制器禁止,於是,連接在同一個IRQ上的其它設備就只有等到該該中斷處理被處理完畢后果才能發出IRQ請求。而采用Bottom_half機制后,不需要立即處理的部分就可以放在下半部分處理,從而,加快了處理機對外部設備的中斷請求的響應速度。
- 還有一個原因就是,處理程序的下半部分還可以包含一些並非每次中斷都必須處理的操作;對這些操作,內核可以在一系列設備中斷之后集中處理一次就可以了。即在這種情況下,每次都執行並非必要的操作完全是一種浪費,而采用Bottom_half機制后,可以稍稍延遲並在后來只執行一次就行了。
由此可見,沒有必要每次中斷都調用下半部分;只有bh_mask 和 bh_active的對應位的與為1時,才必須執行下半部分(do_botoom_half)。所以,如果在上半部分中(也可能在其他地方)決定必須執行對應的半部分,那么可以通過設置bh_active的對應位,來指明下半部分必須執行。當然,如果bh_active的對應位被置位,也不一定會馬上執行下半部分,因為還必須具備另外兩個條件:首先是bh_mask的相應位也必須被置位,另外,就是處理的時機,如果下半部分已經標記過需要執行了,現在又再次標記,那么內核就簡單地保持這個標記;當情況允許的時候,內核就對它進行處理。
如果在內核有機會運行其下半部分之前給定的設備就已經發生了100次中斷,那么內核的上半部分就運行100次,下半部分運行1次。
內核中的某些底半處理過程是和特定設備相關的,而其他一些則更一般一些。表二列出了內核中通用的底半處理過程。
TIMER_BH(定時器) | 在每次系統的周期性定時器中斷中,該底半處理過程被標記為活動狀態,並用來驅動內核的定時器隊列機制。 |
CONSOLE_BH(控制台) | 該處理過程用來處理控制台消息。 |
TQUEUE_BH(TTY 消息隊列) | 該處理過程用來處理 tty 消息。 |
NET_BH(網絡) | 用於一般網絡處理,作為網絡層的一部分 |
IMMEDIATE_BH(立即) | 這是一個一般性處理過程,許多設備驅動程序利用該過程對自己要在隨后處理的任務進行排隊。 |
四.中斷處理全過程
由前面的分析可知,對於0-31號中斷向量,被保留用來處理異常事件;0x80中斷向量用來作為系統調用的總入口點;而其他中斷向量,則用來處理外部設備中斷;這三者的處理過程都是不一樣的。
- 異常的處理全過程
對這0-31號中斷向量,保留用來處理異常事件;操作系統提供相應的異常的處理程序,並在初始化時把處理程序的入口登記在對應的中斷向量表項中。當產生一個異常時,處理機就會自動把控制轉移到相應的處理程序的入口,運行相應的處理程序,進行相應的處理后,返回原中斷處。當然,在前面已經提到,此版本(2.2.5)的Linux只提供了0-17號中斷向量的處理程序。 - 中斷的處理全過程
對於0-31號和0x80之外的中斷向量,主要用來處理外部設備中斷;在系統完成初始化后,其中斷處理過程如下:
當外部設備需要處理機進行中斷服務時,它就會通過中斷控制器要求處理機進行中斷服務。如果 CPU 這時可以處理中斷,CPU將根據中斷控制器提供的中斷向量號和中斷描述符表(IDT)中的登記的地址信息,自動跳轉到相應的interrupt[i]地址;在進行一些簡單的但必要的處理后,最后都會調用函數do_IRQ , do_IRQ函數調用 do_8259A_IRQ 而do_8259A_IRQ在進行必要的處理后,將調用已與此IRQ建立聯系irqaction中的處理函數,以進行相應的中斷處理。最后處理機將跳轉到ret_from_intr進行必要處理后,整個中斷處理結束返回。 -
從數據結構入手,應該說是分析操作系統源碼最常用的和最主要的方法。因為操作系統的幾大功能部件,如進程管理,設備管理,內存管理等等,都可以通過對其相應的數據結構的分析來弄懂其實現機制。很好的掌握這種方法,對分析Linux內核大有裨益。