Linux Kernel 核心中文手冊(7)--中斷和中斷處理

Interrupts and Interrupt Handling (中斷和中斷處理)
 
    本章探討 Linux 核心如何處理中斷。雖然核心有用於處理中斷的通用機制和
接口,大部分中斷處理的細節還是和體系結構相關的。
 
    Linux 使用大量不同的硬件來完成許多不同的任務。顯示設備驅動顯示器,
IDE 設備驅動磁盤等等。你可以同步地驅動這些設備,就是你可以發出一個請求執
行一些操作(比如把一塊內存寫到磁盤)然後等待操作結束。這種方式,雖然可以
工作,但是非常沒有效率,操作系統當它等待每一個操作完成的時候會花費大量時
間“忙於什麼也不做”( busy doing nothing )。一個好的,更有效的方法是做
出了請求然後去作其他更有用的事情,然後當設備完成請求的時候被設備中斷。在
這種方案下,系統中同一時刻可能有許多設備的請求在同時發生。
 
    讓設備中斷 CPU 當前的工作必須有一些硬件的支持。大多數,如果不是所有


的話,通用目的的處理器比如 Alpha AXP 都使用相似的方法。 CPU 的一些物理管
腳的電路只要改變電壓(例如從 +5V 到 -5V )就會讓 CPU 停止正在做的工作,
開始執行處理中斷的特殊代碼:中斷處理代碼。這些管腳之一可能連接一個內部適
中,每一個 1000 分之一秒就接收一箇中斷,其他的也許連接到系統的其他設備,
比如 SCSI 控制器。
 
    系統通常使用一箇中斷控制器把設備的中斷集合在一起,然後把信號傳送到
CPU 的一個單一的中斷管腳。這可以節省 CPU 的中斷管教,也給設計系統帶來了
靈活性。中斷控制器有掩碼和狀態寄存器,用於控制這些中斷。設置掩碼寄存器的
位可以允許和禁止中斷,狀態寄存器返回系統中當前的中斷。
 
    一些系統中的中斷可能是硬連接的,例如實時時鐘的內部時鐘可能永久地連接
到中斷控制器的第 3 管腳。但是,另一些管腳連接什麼可能由在特定的 ISA 或者
 PCI 槽位插入什麼控制卡決定。例如,中斷控制器的第 4 管腳可能和 PCI 槽位
 0 相連,可能某一天有一個以太網卡,當時後來可能是一塊 SCSI 控制卡。每一
個系統都有它自己的中斷中轉機制,操作系統必須足夠靈活才能處理。
 
    大多數現代的通用目的微處理器用相同的方式處理中斷。發生硬件中斷的時候
, CPU 停止它正在運行的指令,跳到內存中一個位置運行,這裏或者包含中斷處
理代碼或者是跳到中斷處理代碼的指令。這種代碼通常在 CPU 的特殊模式下工作
:中斷模式,通常,這種模式下其他中斷不能產生。這裏也有例外:一些 CPU 將
中斷劃分級別,更高級別的中斷可以發生。這意味着寫第一級的中斷處理程序必須


非常小心。中斷處理程序通常都有自己的堆棧,用來存放 CPU 的執行狀態(
CPU 所有的通用寄存器和上下文)並處理中斷。一些 CPU 有一組只在中斷模式下
存在的寄存器,中斷處理代碼可以使用這些寄存器來存儲它需要保存的大部分上下
文信息。
 
 
 
    當處理完中斷, CPU 的狀態恢復,中斷結束。 CPU 會繼續做它在中斷髮生之
前做的事情。重要的事中斷處理程序必須儘可能地有效,通常操作系統不能經常或
者長時間阻塞中斷。
 
7.1 Programmable Interrupt Controllers (可編程中斷控制器)
 
    系統設計師可以任意使用他們希望用的中斷體系結構,但是 IBM PC 都使用
Intel 82C59A-2 CMOS 可編程中斷控制器或者它的衍生物。這種控制器在 PC 最初
的時候就使用了。它可通過寄存器編程,這些寄存器在 ISA 地址空間的衆所周知
的位置。甚至很現代的邏輯芯片組都在 ISA 內存的相同位置保留了等價的寄存器
。非 Intel 的系統,例如 Alpha AXP PC 不受這些體系限制,通常使用不同的中
斷控制器。
 
    圖 7.1 顯示了兩個串聯在一起的 8 位控制器:每一個都有一個掩碼和一箇中
斷狀態寄存器, PIC1 和 PIC2 。掩碼寄存器位於地址 0x21 和 0xA1 ,而狀態寄


存器位於 0x20 和 0xA0 。在掩碼寄存器的一個特殊位寫 1 允許一種中斷,寫
0 可以禁止它。所以向位 3 寫 1 允許中斷 3 ,寫 0 會禁止它。不幸的是(也是
讓人氣惱的),中斷掩碼寄存器只可以寫,你無法讀回你所寫的值。這意味着
Linux 必須爲它設置的掩碼( mask )寄存器保留一份本地拷貝。它在中斷允許和
禁止的例程中修改這些保存的掩碼,每一次都要把整個掩碼寫到寄存器中。
 
    當產生中斷信號,中斷處理程序讀取兩個中斷狀態寄存器( ISR )。它把
0x20 的 ISR 看作 16 位的中斷寄存器的第 8 位, 0xA0 中的 ISR 看作高 8 位
。所以,發生在 0xA0 的 ISR 的第 1 位的中斷被看作是中斷 9 。 PCI1 的第
2 位不可用,因爲它用作串聯 PIC2 的中斷,任何 PIC2 的中斷都會使 PIC1 的第
 2 位置位。
 
7.2 Initializing the Interrupt Handling Data Structures (初始化中斷處理
數據結構)
 
    當設備驅動程序要求控制系統的中斷的時候建立核心的中斷處理數據結構。爲
此,設備驅動程序使用一系列 Linux 核心服務,用來請求一箇中斷、允許它和禁
止它。這些設備驅動程序調用這些例程來登記它們的中斷處理例程的地址。
 
參見 arch/*/kernel/irq.c request_irq() enable_irq() and disable_irq()
 
    PC 體系結構爲了方便把一些中斷固定下來,所以驅動程序在初始化的時候只


需要簡單地請求它的中斷。軟盤設備驅動程序就是這樣:它總是請求中斷 6 。但
是也可能一個設備驅動程序不知道設備會使用什麼中斷。對於 PCI 設備驅動程序
這不是問題,因爲它們總是知道它們的中斷編號。不幸的是對於 ISA 設備沒有什
麼簡單的辦法找到它們的中斷號碼, Linux 允許設備驅動程序探查它們的中斷來
解決這個問題。
 
    首先,設備驅動程序讓設備產生中斷,然後系統中所有沒有分配的中斷都允許
了。這意味着設備等待處理的中斷現在會通過可編程中斷控制器傳遞。 Linux 讀
取中斷狀態寄存器然後把它的內容返回到設備驅動程序。非 0 的結果表示在探查
中發生了一或多箇中斷。驅動程序現在關閉探查,並禁止所有位分配的中斷。如果
 ISA 設備驅動程序成功地找到了它的 IRQ 號,它就可以想平常一樣地請求控制它

 
參見 arch/*/kernel/irq.c irq_probe_*()
 
    PCI 系統比 ISA 系統更加動態。 ISA 設備的中斷通常用硬件設備上的跳線來
設置,對於設備驅動程序是固定的。反過來, PCI 設備的中斷是在系統啓動的時
候由 PCI BIOS 或者 PCI 子系統在 PCI 初始化的時候分配的。每一個 PCI 設備
可以使用 4 箇中斷管腳其中之一: A 、 B 、 C 或 D 。這時設備製造的時候確
定的,大多數設備缺省用中斷管腳 A 。每一個 PCI 槽位的 PCI 中斷線(
interrupte line ) A 、 B 、 C 和 D 都轉到中斷控制器。所以槽位 4 的管腳
 A 可能轉到了中斷控制器的第 6 管腳,槽位 4 的管腳 B 可能轉到了中斷控制器


的管腳 7 ,依此類推。
 
 
 
    PCI 中斷如何被轉發(路由 route )完全是和系統相關的,必須有一些理解
這種 PCI 中斷路由拓撲的設置代碼。在 Intel PC 上,這是啓動的時候的系統
BIOS 代碼完成的。但是對於沒有 BIOS 的系統(例如 Alpha AXP 系統), Linux
 進行這種設置。 PCI 設置代碼把中斷控制器的管腳編號寫到每一個設備的 PCI
配置頭中。它使用它知道的 PCI 中斷路有拓撲和設備的 PCI 槽位以及它正在使用
的 PCI 中斷管腳來決定中斷管腳(或者說 IRQ )編號。設備使用的中斷管腳就確
定下來並放到 PCI 配置頭的一個域。它把這個信息寫到中斷線( interrupte
line )域(這是爲此目的保留的)。當設備驅動程序運行的時候,它讀取這個信
息,並使用它向 Linux 核心請求對中斷的控制。
 
參見 arch/alpha/kernel/bios32.c
 
    系統中可能使用許多 PCI 中斷資源。例如,當使用 PCI-PCI 橋的時候。中斷
來源的數目可能超過系統的可編程中斷控制器的管腳數目。這種情況下, PCI 設
備可以共享中斷:中斷控制器上的一個管腳接收來自多於一個 PCI 設備的中斷。
 Linux 讓第一個請求一箇中斷的源宣稱( declare )它是否可以共享,這樣來支
持中斷共享。共享中斷結果是 irq_action 向量表中的一個條目可以指向幾個
irqaction 的數據結構。當發生了一個共享的中斷的時候, Linux 會調用這個源


的所有的中斷處理程序。所有可以共享中斷的設備驅動程序(都應該是 PCI 設備
驅動程序)必須預備在沒有中斷服務的時候被調用。
 
7.3 Interrupt Handling (中斷處理)
 
    Linux 中斷處理子系統的一個主要任務是把中斷轉送到( route )正確的中
斷處理代碼段。這種代碼必須瞭解系統的中斷拓撲。例如,如果軟驅控制器在中斷
控制器的管腳 6 發生中斷,它必須可以識別出中斷是來自軟驅,並把它轉送到軟
驅設備驅動程序的中斷處理程序代碼。 Linux 使用一系列數據結構的指針,包含
了處理系統中斷的例程的地址。這些例程屬於系統中的設備的設備驅動程序,每一
個設備驅動程序必須負責在驅動程序初始化的時候請求它想要的中斷。圖 7.2 顯
示了 irq_action 是一個指向 irqaction 數據結構的指針的向量表。每一個
irqaction 數據結構都包括了這個中斷處理程序的信息,包括中斷處理例程的地址
。不同體系的中斷數目和如何處理是不同的,通常,不同系統之間, Linux 中斷
處理代碼是和體系結構相關的。這意味着 irq_action 向量表的大小依賴於中斷源
的數目而不同。
 
    當發生中斷的時候, Linux 必須首先通過讀取系統的可編程中斷控制器的狀
態寄存器確定它的來源。然後把這個來源轉換成 irq_action 向量表中的偏移。例
如,從軟驅控制器來的中斷控制器管腳 6 的中斷會轉爲中斷處理程序向量表中的
第 7 個指針。如果發生的中斷沒有對應的中斷處理程序, Linux 核心會記錄下一
個錯誤,否則,它會調用這個中斷源的所有的 irqaction 數據結構中的中斷處理


    當 Linux 核心調用設備驅動程序的中斷處理例程的時候,它必須有效地判斷
爲什麼被中斷,並進行響應。爲了找出中斷的原因,設備驅動程序會讀取中斷設備
的狀態寄存器。設備可能迴應:發生了一個錯誤或者完成了一個請求的操作。例如
軟驅控制器可能報告它已經把軟驅的讀磁頭定位到了軟盤正確的扇區。一旦確定了
中斷的原因,設備驅動程序可能還需要做更多的工作。如果是這樣, Linux 核心
有機制允許延遲這個操作稍候進行。這可以避免讓 CPU 在中斷模式下花費太多時
間。詳細描述參見設備驅動程序章(第 8 章)

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