Linux Kernel中斷漫談 (上)
1. 穿透嘆息之牆
嘆息之牆
出自《聖鬥士星矢》,在冥王哈迪斯掌管的冥界盡頭,有一道巨大的牆壁,嚴實地堵住了通往極樂世界的通道,除了神,任何人都無法通過這道牆壁,眼看一牆之隔的那方便是極樂凈土,卻又無法前往,無論是誰面對這道高強,都只能留下無比的遺憾和嘆息,因此稱為「嘆息之牆」。「嘆息之牆」比世界上任何銅牆鐵壁都要更加堅固,任何外力都不可能直接將其摧毀,除非有陽光的照入才有可能將其穿透,但是一向死寂漆黑的冥界里,何來陽光呢?
暴露年齡的引用。
中斷是穿透Hardware和Software之間「嘆息之牆」的一種媒介。在嘆息之牆的這邊是磁碟,滑鼠,鍵盤等等實體;而在另一端是操作系統,驅動或者某個簡單的嵌入式程序。當然,中斷並不是唯一的方式,具體而言,有三種經典的方式:
輪詢: 周期性的查詢外部設備是否可用。缺點是浪費是CPU資源。但是在CPU資源富裕(非計算密集型)或者careful design的情況下,能提供比中斷更佳好的性能。比如network中的NAPI(new API)就會在收集到足夠多的報文以後,一次性中斷CPU,防止每次報文reach都需要中斷CPU
中斷:通過設備的中斷線路來提醒CPU,sth happends。中斷的實現需要硬體級別的支持,包括設備,中斷晶元和CPU。CPU會在每個cycle的末尾作一次中斷的check(硬體級別的check,任何cycle都會有,所以沒有性能影響)
DMA:單獨的DMA晶元去負責數據傳輸。對於磁碟之間大數據量的交換非常有效果。DMA會首先去申請匯流排,if成功,那麼會使用匯流排,用完歸還。DMA只會在所有數據處理完畢後,通過中斷通知CPU。
早期嵌入式設備的埠可能支持輪詢,而更加新的埠可以使用DMA的模式在完成大塊數據的傳輸工作。
2. 中斷
Wikipedia上關於中斷定義很簡單:
In system programming, an interrupt is a signal to the processor emitted by hardware or software indicating an event that needs immediate attention.
對於Linux而言,中斷進一步準確的分為:
- 異常(excepption):由CPU內部引起,也叫同步中斷,不能被CPU屏蔽,細分分為:1.1. Fault(出錯):可恢復,恢復後回到出發Fault的那條指令繼續執行,比如 Page Fault,在page被load進內存後繼續執行剛才執行的命令1.2. Trap(陷入):可恢復,恢復後執行觸發Trap指令的後一條指令。比如除0錯誤,或者Invalid Memory Address Reference。也稱為硬陷入(Hardware Trap),對比軟陷入(Software Trap)1.3. Abort(中止):無法恢復,比如發生了硬體問題。
- 中斷:也叫非同步中斷,由外部設備引起,細分為可屏蔽中斷(INTR)和非可屏蔽中斷(NMI)
以上是真正意義上的中斷,有各自的IRQ向量和處理函數,也稱為硬中斷(Hardware Interrupt)。對於X86有兩根中斷的引腳,一根非屏蔽中斷線(包括異常),一根可中斷線(接到中斷處理晶元)。其中非屏蔽中斷線多半是intel內部保留;可中斷線一般給外設使用。
對於Liunx而言,它提供了一些的syscall(INT 0x80),通過類似中斷的機制陷入到Kernel Space完成需要的工作(比如讀取文件),一般稱之為軟中斷(Software Interrupt)。
基於Intel晶元的Linux對於中斷按照:異常 -> 非可屏蔽中斷 -> 可屏蔽中斷 -> 軟中斷的順序進行中斷向量的分布:
- 0-15 異常,intel內部保留,不可屏蔽
- 16-31 非屏蔽中斷,intel內部保留,不可屏蔽
- 32-47 可屏蔽中斷,可屏蔽
- 47-255 軟中斷,linux系統本身使用128(0x80)來作為系統調用軟中斷,可屏蔽
2.1. x86中斷子系統
X86通過(Intel 8259A)兩級晶元或者新的APIC(Advanced Programmable Interrupt Controller )來註冊設備。在real mode支持15個中斷IRQ0 - IRQ15 (IRQ2被佔用)。其中部分的中斷號已經被固定(比如給鍵盤),無法改變,其他的映射到系統中的各個設備(埠)上。
Intel 8259A晶元只適用於uniprocessor的系統,對於SMP系統,新的APIC晶元被引入。在APIC中,原有的IRQ0 - IRQ15分配不一定保留。
Intel 8259A的IRQ0 - IRQ15一個典型分配如下:
Master PIC
- IRQ 0 – system timer (cannot be changed)IRQ 1 – keyboard controller (cannot be changed)
- IRQ 2 – cascaded signals from IRQs 8–15 (any devices configured to use IRQ 2 will actually be using IRQ 9)
- IRQ 3 – serial port controller for serial port 2 (shared with serial port 4, if present)
- IRQ 4 – serial port controller for serial port 1 (shared with serial port 3, if present)
- IRQ 5 – parallel port 2 and 3 or sound card
- IRQ 6 – floppy disk controller
- IRQ 7 – parallel port 1. It is used for printers or for any parallel port if a printer is not present. It can also be potentially be shared with a secondary sound card with careful management of the port.
Slave PIC
- IRQ 8 – real-time clock (RTC)
- IRQ 9 – Advanced Configuration and Power Interface (ACPI) system control interrupt on Intel chipsets.[1] Other chipset manufacturers might use another interrupt for this purpose, or make it available for the use of peripherals (any devices configured to use IRQ 2 will actually be using IRQ 9)
- IRQ 10 – The Interrupt is left open for the use of peripherals (open interrupt/available, SCSI or NIC)
- IRQ 11 – The Interrupt is left open for the use of peripherals (open interrupt/available, SCSI or NIC)
- IRQ 12 – mouse on PS/2 connector
- IRQ 13 – CPU co-processor or integrated floating point unit or inter-processor interrupt (use depends on OS)
- IRQ 14 – primary ATA channel (ATA interface usually serves hard disk drives and CD drives)
- IRQ 15 – secondary ATA channel
其中IRQ0(時鐘中斷)和IRQ1(鍵盤中斷)這兩個中斷即使在APIC中依然被保留。
root@julyhou:~# cat /proc/interruptsn CPU0 CPU1 CPU2 CPU3n 0: 126 0 0 0 IR-IO-APIC-edge timern 1: 2 0 0 0 IR-IO-APIC-edge i8042n 8: 1 0 0 0 IR-IO-APIC-edge rtc0n 9: 0 0 0 0 IR-IO-APIC-fasteoi acpin 12: 4 0 0 0 IR-IO-APIC-edge i8042n
可以看到IRQ0,IRQ1,IRQ8和IRQ9和Intel 8259A中提供一樣的功能,其他的IRQ都沒有被分配。i8042是鍵盤中斷(PS/2),有兩個是因為設計時候兼容滑鼠,所以IRQ1給鍵盤,一個IRQ12給滑鼠。
2.1.1. Intel 8259A晶元
Intel 8259A晶元通過2級橋接(slave晶元接在master的IRQ2上),提供總共15個中斷。可用的其實更加少,因此在某個階段(APIC還沒出現),Intel提供了IRQ共享的概念,通過添加額外的線路(PIRQ)來使得多個PCI設備使用同一個IRQ隨後通過軟體的模式來具體區分是哪一個設備觸發了中斷。單個IRQ最多可以有8個共享(PIRQA - PIRQH)。
注意晶元本身也是有時鐘的,默認是。晶元通過引腳INTR來告知CPU有中斷到來(CPU通過引腳INTA來反饋信息);通過D0 - D7來傳輸數據。晶元的可寫寄存器通過IN/OUT指令來操作。
IRQ同時也代表了中斷的優先順序,數字越小,優先順序越高。
2.2.2. APIC
伴隨著SMP系統的誕生,APIC晶元被引入。APIC包括兩種類型晶元:
- local APIC,本地(局部)APIC晶元和CPU Core 一一對應
- I/O APIC,負責鏈接設備,並發送給local APIC。最多有8個,所有CPU Core共享。
local APIC和I/O APIC之間通過FSB相互連接(早期有單獨的APIC匯流排)。
Intel 8259A也可以和APIC橋接:
In systems containing an 8259 PIC, the 8259 may be connected to the LAPIC in the systems bootstrap processor (BSP), or to one of the systems I/O APICs, or both. Logically, however, the 8259 is only connected once at any given time.
APIC有兩種模式:
- 作為一種標準的 8259A 工作方式。本地 APIC 被禁止,外部 I/O APIC 連接到 CPU,兩條 LINT0 和 LINT1 分別連接到 INTR 和 NMI 引腳。
- 作為一種標準外部 I/O APIC。本地 APIC 被激活,且所有的外部中斷都通過 I/O APIC 接收。
APIC提供更加多的IRQ,單個I/O APIC晶元支持24個IRQ。I/O APIC類似於一個dispatcher,通過一定的規則,按照中斷的affinity設置來派發每個中斷。
同時APIC還提供了IPI(Inter Processor Interrupt)功能,用於發送中斷給其他Core,可軟體觸發。
- send_IPI_all() : Sends an IPI to all CPUs (including the sender)
- send_IPI_allbutself() : Sends an IPI to all CPUs except the sender
- send_IPI_self() : Sends an IPI to the sender CPU
- send_IPI_mask() : Sends an IPI to a group of CPUs specified by a bit mask
APIC定義的中斷觸發的時機有兩種:Edge triggered(邊緣觸發) or Level triggered(狀態)
- Edge triggered(邊緣觸發)意味著中斷信息的發送是通過電平轉換來觸發的(上升沿或者下降沿),也就是類似於脈衝信號。這意味著信號如果沒有被接受就會丟掉。多個統一中斷向量的中斷觸發會產生多個中斷信息。
- Level triggered(狀態)是通過維持電平來觸發的。系統會周期性的檢查引線的電平,如果符合要求(比如高電平),那麼就認為中斷向量被觸發。在電平未清除之前,重複觸發會被ignore掉(因為一直在高電平)。
準確的說edge分為low-to-high edge triggered和 high-to-low edge triggered;level分為active high level-sensitive和active low level-sensitive。具體需要查看各個晶元的文檔。
查看/proc/interrupts,會發現兩種不同APIC的設置:apic-edge 和 apic-fasteoi。前者如其名字,代表edge triggered;後者是level trigger,並且中斷的狀態會保持,直到EOI(end of interrupt,代表中斷處理完畢)被PIC(programmable Interrupt Controller)接收到,才會被清除。
2.2. ARM中斷子系統
ARM中斷依賴於Generic Interrupt Controller(GIC),每一代都會有不停的演進,支持的中斷數目也不同:比如A15的CoreLink GIC-400 Generic Interrupt Controller,支持256個中斷向量。具體分區需要參考各個晶元的文檔,以下GICv2為例子。(後續的GICv3,GICv4文檔ARM沒有公布,當然也可以使用其他類型的中斷晶元)
GICv2可以管理四類中斷類型(CPU內部中斷它不管):
- Peripheral Interrupt(外圍中斷)
- Private Peripheral Interrupt (PPI) :發送給特定CPU的中斷
- Shared Peripheral Interrupt (SPI) :可以route給任何一個CPU處理的中斷
- Software-generated interrupt (SGI) :類似APIC的IPI中斷,負責Processor之間交互
- Virtual Interrupt:給Virtual Machine使用的,比較少見。
- Maintenance Interrupt:類似於GIC內部的中斷,用來enable/disable GIC一些功能。
GICv2指定ID0 - ID31為PPI,ID32往上時SPI,根據晶元類型,甚至支持上千的SPI(雖然A15隻有224個)。
ARM沒有向前兼容的需求,所以很多時候可以設計的更加激進一些。
另外ARM Core的state要比x86多很多(多達7個),其中有兩個專門給interrupt提供的state(知乎表格的功能什麼時候能提供呢?!):
其中FIQ(快速中斷)和IRQ是ARM專門提供來處理中斷。FIQ的優先順序高於IRQ,並且處理時排斥其他FIQ或者IRQ。
ARM的Vector Table一共7格:
Interrupt/Exception/ResettModetAddressnResetttttSVCt0x00000000nUndefined instructionttUNDt0x00000004nSoftware interrupt (SWI)tSVCt0x00000008nPrefetch aborttttABTt0x0000000cnData aborttttABTt0x00000010nReservedtttN/At0x00000014nIRQttttIRQt0x00000018nFIQttttFIQt0x0000001cn
對於FIQ/IRQ,多個handler通過類似link list的方式串起來,一個中斷會按照順序從頭搜索handler,直到找到可以識別和處理的handler。
2.4. SMP IRQ affinity
類似於CPU core的affinity,中斷也可以指定的綁定到一個或者一批特定的core上執行。
/proc/irq/IRQ#/smp_affinity and /proc/irq/IRQ#/smp_affinity_list specify
which target CPUs are permitted for a given IRQ source. Its a bitmask
(smp_affinity) or cpu list (smp_affinity_list) of allowed CPUs. Its notallowed to turn off all CPUs, and if an IRQ controller does not supportIRQ affinity then the value will not change from the default of all cpus.
通過查看/proc/interrupts,如果發現某個core busying handle 某個中斷,那可以改變這個中斷變數的affinity,讓其他core分擔中斷的壓力。
不過,SMP IRQ affinity需要kernel和APIC的合作,所以如果可能太老,不一定能夠work。這種情況下,請詢問是否可以升級kernel。
推薦閱讀:
※Linux為什麼要衍生出那麼多的版本,統一一下產品線不好么?
※Linux文件亂碼
※Linux哪種文件系統更有前景?
※如何學會使用 Linux 操作系統?
※linux掛載機制,訪問父目錄的細節是怎樣的?