第七章 中斷和中斷處理
1. 異常和中斷
-
異常:必須考慮與處理器時鐘同步,由軟件產生,亦稱爲同步中斷。如除零異常和缺頁異常
-
中斷:由硬件產生的異步中斷
2. 中斷處理程序
-
中斷處理程序是被內核調用來響應中斷的,運行與中斷上文。中斷上下文又稱原子上下文,該上下文的執行代碼不可阻塞。
-
最起碼,中斷處理程序要負責通知硬件設備中斷已被接受:嗨,硬件,我聽到你了,現在回去工作吧!
3. 上半部和下半部
-
中斷處理程序是上半部(top half)——接收到一個中斷,它就立即開始執行,但只做有嚴格時限的工作,例如對接受的中斷進行應答或者復位硬件,這些工作都是在所有中斷被禁止的情況下完成的。能夠被運行稍後完成的工作會推遲到下半部(bottom half)去。此後,在合適的時機,下半部會開中斷執行。
4. 在註冊中斷時IRQF_SHARED和dev的作用
-
IRQF_SHARED:表示在多個中斷處理程序之間共享中斷線,共享中斷線的中斷處理程序會存放在同一個中斷對應的鏈表中,用dev來區分,且dev全局唯一。在內核接受一個中斷後,它將依次調用在該中斷線上註冊的每一個中斷處理程序,在中斷處理程序中需要通過查詢硬件設備提供的狀態寄存器或者其他機制來確定這個中斷是否爲共享這個中斷線的其他設備發出的中斷,如果是,就不用處理,立即退出。
-
dev:如果是共享中斷線,必須設置這個標誌,而且必須全局唯一。在執行中斷處理函數時會將dev傳遞給中斷處理程序。在釋放共享中斷時,也會用到dev,用來指定需要刪除那個中斷處理函數。對於非共享中斷線的情況,dev參數可以用來區分共享同一個中斷處理程序的多個設備。
-
IRQF_DISABLED: 如果設置了,內核在處理中斷處理程序程序本身期間,有禁止當前CPU的所有其他的中斷。否則,中斷處理程序可以與除本身外的其他任何中斷同時運行。在最新的kernel中已經沒有這個標誌了。
5. request_irq() 函數可能會睡眠,故不能用在中斷上下文以及其他不允許阻塞的代碼中
6. 初始化硬件和註冊中斷處理程序的順序必須正確,以防止中斷處理程序在設備初始化完成之前就開始執行。
7. 中斷處理程序的可重入性:
-
Linux中的中斷處理程序是無需重入的,當一個給定的中斷中斷處理程序正在執行時,相應的中斷線在所有的處理器上都會被屏蔽掉,以防止在同一中斷線上接收另一個新的中斷。通常情況下,所有的其他中斷都是打開的,所以這些不同中斷線上的其他中斷都能被處理,但是當前中斷線總是被禁止的。
-
如果是多個中斷線共享同一個中斷處理程序就需要考慮可重入性了。
8. 在進程上下文可以通過current宏關聯當前進程。在中斷上下文中,current宏指向被中斷的進程。
9. 中斷上下文不能睡眠,進程上下文可以睡眠。
10. 中斷處理程序棧:
-
共享所中斷進程的內核棧,內核棧大小爲兩個連續的物理頁。對於32位系統爲8KB,對於64位系統是16KB。
11. /proc/interrupts
存放的是系統中與中斷相關的統計信息。如果:
CPU0 CPU1 CPU2 CPU3
14: 424 0 0 0 s3c-timer 1-wire TimerTick
16: 30 0 0 0 s3c-uart s5pv210-uart
...
98: 0 0 0 0 GIC s3c-pl330.0
...
126: 0 0 0 0 GIC s3c2440-i2c.4
129: 0 0 0 0 GIC s3c2440-i2c.7
134: 6425 0 0 0 GIC ehci_hcd:usb1, ohci_hcd:usb2
說明: 第一列是中斷線,沒有顯示沒有安裝處理程序的中斷線
第二、三、四、五列是這個中斷線在對應的cpu上的接收數目
第六列是處理這個中斷的中斷控制器
第七列是與這個中斷相關的設備名字,這個名字是通過參數devname提供給函數request_irq的
其中第9行中表示“ehci_hcd:usb1”與“ohci_hcd:usb2”是共享中斷線
12. 中斷控制
-
控制中斷系統的原因:需要提供同步
-
通過禁止中斷,可以確保某個中斷處理程序不會搶佔當前的代碼
-
禁止中斷還可以禁止內核搶佔。(系統滴答中斷被禁,調度程序無法自發選擇下一個進程調度)
-
禁止中斷和禁止內核搶佔,都不能法制來自其他CPU的併發訪問。
-
內核代碼一般通過獲取某種鎖防止來自其他CPU對共享數據的併發訪問。在獲取這些鎖的同時也伴隨着禁止本地中斷。
-
鎖提供的保護機制,防止來自其他CPU的併發訪問,而同時禁止中斷提供保護機制,則是防止來自其他中斷處理程序的併發訪問。(可以參考中斷處理程序的可重入性)
-
禁止本地中斷
-
用於禁止當前CPU上的本地中斷
-
local_irq_disable() 和 local_irq_enable()
-
這兩個函數不能恢復被調用前當前CPU的中斷禁止使能狀態,並且不受次數影響。
-
下面的local_irq_save和local_irq_restore,用法:
-
unsignedlong flags;
local_irq_save(flags);
...
local_irq_restore(flags);
-
需要注意: local_irq_save和local_irq_restore必須在同一個函數中進行
-
注:上面的四個函數即可用於中斷上下文,也可用於進程上下文
-
上面的函數操作的是某個CPU內部寄存器,其他CPU不受影響,系統中斷控制器也不受影響
-
禁止指定中斷線
-
只禁止整個系統(所有CPU)中的一條特定的中斷線
-
涉及到如下四個函數:
-
void disable_irq(unsignedint irq);
void disable_irq_nosync(unsignedint irq);
void enable_irq(unsignedint irq);
void synchronize_irq(unsignedint irq);
-
disable_irq只有在被禁止的中斷線上的所有處理程序完成後才能返回。不僅要確保不在指定的中斷線上傳遞新的中斷,而且還要確保所有已經開始執行的處理程序已經完全退出
-
disable_irq_nosync不會等待當前中斷處理程序已經退出,只需要確保不在指定的中斷線上傳遞新的中斷
-
synchronize_irq等待一個特定的中斷處理程序退出。如果該處理程序正在執行,那麼該函數必須退出後才能返回
-
在實現上,disable_irq調用了disable_irq_nosync和synchronize_irq,如下:
-
void disable_irq(unsignedint irq)
{
if(!__disable_irq_nosync(irq))
synchronize_irq(irq);
}
-
對disable_irq或diable_irq_nosync的每次調用,都需要相應的調用一個enable_irq。只有在enable_irq完成最後一次調用後,才真正重新激活了中斷線
-
注:上面的四個函數既可用於中斷上下文,也可以用於進程上下文
-
上面的disable_irq和enable_irq控制的都是中斷控制器上指定的中斷線,即禁止或者使能給定中斷向系統中所有CPU的傳遞,而不是操作CPU內部的寄存器
13. 中斷系統的狀態
-
irqs_disabled() : 如果本地中斷被禁止,返回非0,否則返回0
-
in_interrupt() : 如果正在執行中斷處理程序或者正在執行下半部處理程序,則返回非0。否則返回非0
-
in_irq(): 如果正在執行中斷處理程序,則返回非0,否則返回0
完。
本文来自博客园,作者:摩斯电码,未经同意,禁止转载