5中斷處理

上一篇博文裏,我們談到了異常處理,現在我們開始研究一下中斷處理的情況。中斷處理比異常處理複雜得多,這是因爲:

 

第一,中斷的發生對於正在運行的進程無關,被調用的中斷處理函數叫做中斷服務程序,它運行在內核態並處於系統上下文中(使用內核頁表、其爲內核代碼和內核數據結構),所以中斷處理程序不允許被阻塞;

 

第二,由於硬件資源的限制,比如APIC的IRQ引腳數量有限,一根IRQ信號線需要被幾個外部設備所共享,因此當一個IRQ信號產生時CPU不可能會預先知道是哪一個外部設備發出的中斷請求,故要求內核必須提供一個能夠爲共享一根IRQ線上的幾個設備提供服務的中斷處理程序;

 

第三,由於執行中斷處理程序時不允許被阻塞,所以要求中斷處理程序的代碼儘可能短,以求快速執行完畢而不影響整個系統的性能。

針對第三點,在這裏還要強調一下。當中斷髮生時,並不是與中斷相關的所有操作都具有同等的緊迫性。Linux是這樣解決的:把中斷髮生後需要CPU執行的操作分爲“快速中斷處理”和“慢速中斷處理”兩部分。

 

快速中斷處理又再次被劃分成兩類,一類是那些需要CPU緊急處理的操作——如修改由設備和數據結構同時訪問的數據結構、對中斷控制器或設備控制器重新編程等情況——這類處理動作需要在關中斷的情況下執行,即CPU的中斷標誌位IF被禁止,不允許其他中斷源所中斷;還有一類快速中斷處理是一些不是那麼緊急但不能延遲的操作——如鍵盤上有某個鍵被按下後,讀掃描碼這個操作——這類中斷處理里程序實在開中斷的情況下進行的,因爲這類中斷操作只修改CPU纔會訪問到的數據。

 

慢速中斷處理是指那些不是太緊急同時在一定時間範圍內可延遲的中斷操作,例如把一個關中斷期間拷貝到某緩衝區(位於接口的緩存中)的內容再複製到進程的地址空間。需要這些數據的進程會“耐心”等待,因此這類操作可以延遲較長時間,由內核在適當的時候調度他們運行。這類慢速處理動作用到的就是著名的Linux“下半部分”函數。顯然,這類操作也是在開中斷的情況下執行的。

 

中斷向量

 

我們先來回顧一下中斷的分類。每個中斷和異常是由0~255之間的一個數來標識的,我們把這個八位無符號整數叫做向量。非屏蔽中斷的向量和異常的向量是固定的,而可屏蔽中斷的向量可以通過對中斷控制器的編程來改變。向量0-19號表示20個異常,32-238表示物理號IRQ,其中的128號用作系統調用,239號用作APIC 時鐘中斷等。

 

IBM PC兼容的體系結構要求,一些設備必須被固定地連接到指定的IRQ線。典型的情況是:
- 間隔定時設備必須連到IRQ0線。
- 從8259A PIC必須與IRQ2線相連(儘管現在有了更高級的PIC,Linux還是支持8259A風格的PIC)。
- 必須把外部數學協處理器連接到IRQ 13線(儘管最近的80x86處理器不再使用這樣的設備,但Linux仍然支持歷史悠久的80386模型)。

 

那麼,除了上述一些典型情況,其他一般的IRQ可配置設備選擇一條線則有三種方式:
1、設置一些硬件跳接器(僅適用於舊式設備卡)。
2、安裝設備時執行一個實用程序。這樣的程序可以讓用戶選擇一個可用的IRQ號,或者探測系統自身以確定一個可用的IRQ號。
3、在系統啓動時執行一個硬件協議。外設宣佈它們準備使用哪些中斷線,然後協商一個最終的值以儘可能減少衝突。該過程一旦完成,每個中斷處理程序都通過訪問設備某個I/O端口的函數,來讀取所分配的IRQ。例如,遵循外設部件互連(Peripheral Component Interconnect, PCI)標準的設備的驅動程序利用一組函數,如pci_read_config_byte()訪問設備的配置空間。

 

內核必須在啓用中斷前發現IRQ號與I/O設備之間的對應,否則,內核在不知道哪個向量對應哪個設備的情況下,無法處理來自這個設備的信號。IRQ號與I/O設備之間的對應是在初始化每個設備驅動程序時建立的(後面講設備驅動的時候會提及具體的方法)。

 

下表就是把IRQ分配給I/O設備的一個例子,這也是大多數80x86體系初始化後的實例:

 

IRQ

INT

硬件設備

0

32

時鐘

1

33

鍵盤

2

34

PIC級聯

3

35

第二串口

4

36

第一串口

6

38

軟盤

8

40

系統時鐘

10

42

網絡接口

11

43

USB端口、聲卡

12

44

PS/2鼠標

13

45

數學協處理器

14

46

EIDE磁盤控制器的一級鏈接

15

47

EIDE磁盤控制器的二級鏈接

 

 

IRQ數據結構

 

不管引起中斷的電路種類如何,所有的I/O中斷處理程序都執行四個相同的基本操作:
1.在內核態堆棧中保存IRQ的值和寄存器的內容。
2.爲正在給IRQ線服務的PIC發送一個應答,這將允許PIC進一步發出中斷。
3.執行共享這個IRQ的所有設備的中斷服務例程(ISR)。
4.跳到ret_from_intr()的地址後終止。

 

這裏重點談談第3個操作。大家必須得明確一個概念,一個IRQ不等於一個設備,他們是一個一對多的關係,這一點太重要了。幾個設備可以共享一個IRQ線,這就意味着僅僅中斷向量不能說明所有問題。在上面那個例子中,同一個向量43既分配給USB端口,也分配給聲卡。不過,在老式PC體系結構(像ISA)中發現的一些硬件設備,當它們的IRQ與其他設備共享時,就不能可靠地運轉。

 

所以,第三步操作執行的是給定IRQ對應的所有設備的中斷服務例程。這是爲什麼呢?因爲內核不可能預先知道是哪個特定的設備產生的本個IRQ,因此,每個ISR都被執行,以驗證它的設備是否需要關注,如圖所示;如果是,就執行需要執行的所有操作。

 

共享IRQ

 

還有一種情況——IRQ動態分配。跟多個設備共享一個IRQ不同,一條IRQ線在可能的最後時刻才與一個設備驅動程序相關聯;例如,軟盤設備的IRQ線只有在用戶訪問軟盤設備時才被分配。這樣,即使幾個硬件設備並不共享IRQ線,同一個IRQ向量也可以由這幾個設備在不同時刻使用(見本博最後一部分的討論)。

 

每個中斷向量都有一個irq_desc_t描述符,其具體字段如下。所有的這些描述符組織在一起形成irq_desc數組。

 

字段

說明

handler

指向PIC對象(hw_irq_controller描述符),它服務於IRQ線

handler_data

指向PIC方法所使用的數據

action

標識當出現IRQ時要調用的中斷服務例程。該字段指向IRQ的irqaction描述符鏈表的第一個元素。在本章後面將描述irqaction描述符。

status

描述IRQ線狀態的一組標誌

depth

如果IRQ線被激活,則顯示0;如果IRQ線被禁止了不止一次,則顯示一個正數

irq_count

中斷計數器,統計IRQ線上發生中斷的次數(僅在診斷時使用)

irqs_unhandled

對在IRQ線上發生的無法處理的中斷進行計數(僅在診斷時使用)

lock

用於串行訪問IRQ描述符和PIC的自旋鎖

 

irq_desc_t描述符的depth字段和IRQ_DISABLED標誌表示IRQ線是否被禁用。每次調用disable_irq()或disable_irq_nosync()函數,depth字段的值增加,如果depth等於0,函數禁用IRQ線並設置它的IRQ_DISABLED標誌。相反,每當調用enable_irq()函數,depth字段的值減少,如果depth變爲0,函數激活IRQ線並清除IRQ_DISABLED標誌。

 

在系統初始化期間,init_IRQ()函數把每個IRQ主描述符的status字段設置成IRQ_DISABLED。此外,init_IRQ()通過替換由setup_idt()所建立的中斷門來更新IDT,上上篇博文的謎底就是在這裏解開了。

 

下面重點來關注一下irq_desc_t描述符的handler字段。由於現代PC體系中,中斷控制器已經不再單純是8259A芯片系列了,還支持一下其他的PIC電路,因此,Linux用了一個“PIC對象”,由PIC名字和7個PIC標準方法組成。這種面向對象方法的優點是,驅動程序不必關注安裝在系統中的PIC種類。每個驅動程序可見的中斷源透明地連接到適當的控制器。定義PIC對象的數據結構叫做hw_interrupt_type(也叫做hw_irq_controller)。

 

這裏很難理解,我們還是用老方法,舉個實例來說明他。假設,我們的系統只是兩片8259A級聯出來的PIC,則在這種情況下,只有16個irq_desc_t描述符,其中每個描述符的handler字段指向描述8259A PIC的i8259A_irq_type變量。這個變量被初始化爲:

    struct hw_interrupt_type i8259A_irq_type = {
        .typename     = "XT-PIC",
        .startup      = startup_8259A_irq,
        .shutdown     = shutdown_8259A_irq,
        .enable       = enable_8259A_irq,
        .disable      = disable_8259A_irq,
        .ack          = mask_and_ack_8259A,
        .end          = end_8259A_irq,
        .set_affinity = NULL
    };

 

這個結構中的第一個字段“XT-PIC”是PIC的名字。接下來就是用於對PIC編程的六個不同的函數指針。前兩個函數分別啓動和關閉芯片的IRQ線。但是,在使用8259A芯片的情況下,這兩個函數的作用與第三、四個函數是一樣的,第三、四個函數是啓用和禁用IRQ線。mask_and_ack_8259A()函數通過把適當的字節發往8259A I/O端口來應答所接收的IRQ。end_8259A_irq()函數在IRQ的中斷處理程序終止時被調用。最後一個set_affinity()方法置爲空:它用在多處理器系統中以聲明特定IRQ所在CPU的“親和力”——也就是說,那些CPU被啓用來處理特定的IRQ。

 

下面再來談談irq_desc_t描述符的action字段。如前所述,多個設備能共享一個單獨的IRQ。因此,內核要維護多個irqaction描述符,其中的每個描述符涉及一個特定的硬件設備和一個特定的中斷。包含在這個描述符中的字段如下表所示。

 

字段

說明

handler

指向一個I/O設備的中斷服務例程。這是允許多個設備共享同一IRQ的關鍵字段

flags

描述IRQ與I/O設備之間的關係

mask

未使用

name

I/O設備名(通過讀/proc/interrupts文件,在列出所服務的IRQ時也顯示設備名)

dev_id

I/O設備的私有字段。典型情況下,它標識I/O設備本身(例如,它可能等於其主設備號和次設備號),或者它指向設備驅動程序的數據

next

指向irqaction描述符鏈表的下一個元素。鏈表中的元素指向共享同一IRQ的硬件設備

irq

IRQ線

dir

指向與IRQn相關的lproclirgln目錄的描述符

 

好啦,中斷處理的數據結構介紹完了,下面以一個圖來總結並且結束這篇博文:

 

中斷數據結構


發佈了0 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章