Linux核心學習之中斷
[中斷概述]
中斷本質上是一種特殊的電訊號,由硬體裝置發向處理器。異常和中斷的不同是異常在產生時必須考慮與處理器時鐘同步。實際上異常也常常稱為同步中斷。比如在除0或者缺頁時,必須靠核心處理的時候,處理器就會產生一個異常。
[中斷處理機制的實現]
中斷從硬體到核心的路由
裝置產生中斷,通過匯流排把電訊號發送給中斷控制器。如果中斷線是啟用的(它們允許被屏蔽的),那麼中斷控制器就會把中斷髮往處理器。在大多數體繫結構中,這個工作就是通過電訊號給處理器的特定管腳發送一個訊號。除非在處理器上禁止該中斷,否則處理器會立即停止它正在做的事,關閉中斷系統,然後跳到記憶體中預定的位置開始執行那裡的代碼。這個預定義的位置是由核心設定的,是中斷處理常式的進入點。
[中斷處理常式]
在響應一個特定中斷的時候,核心會執行一個函數,該函數叫做中斷處理常式(interrupt handler)也叫做插斷服務常式(interrupt service routine)。
中斷處理常式就是一個普普通通的C函數,但是它與其他核心功能的真正區別在於,中斷處理常式是被核心調用來響應中斷的,它運行於中斷上下文中。
註冊中斷處理常式:
static inline int __must_check
request_irq(unsigned int irq, irq_handler_thandler, unsigned long flags,
const char *name, void *dev)
irq:分配的中斷號
handler:中斷處理函數程式的指標 typedefirqreturn_t (*irq_handler_t)(int, void *);
flags:中斷類型
name:與中斷相關的裝置的名字;
dev:用於共用中斷,因為可能在一條中斷線上有幾個裝置,dev用來區分是哪個裝置產生的中斷
中斷上下文和進程上下文對比
|
中斷上下文 |
進程上下文 |
定義 |
當執行一個中斷處理常式時,核心處於中斷上下文中 |
當程式調用了系統調用或者觸發了某個異常,它就陷入了核心空間,此時,核心代表進程執行並處於進程上下文中。 |
睡眠情況 |
不可睡眠,不能被調度,也就是說中斷上下文中不能使用有可能睡眠的函數 |
可睡眠,可調度 |
同步機制 |
自旋鎖 |
都可 |
|
|
|
|
|
|
[中斷上半部和下半部]
首先問自己一個問題,為什麼要把中斷分為上半部和下半部,難道一個就放在中斷處理常式中不好嗎?答案是否定的
中斷的劃分為我們解決了即想中斷處理常式運行得快,又想中斷處理常式完成的工作量多。
1、中斷可以隨時的打斷其他正在執行的程式,如果被打斷的代碼對系統很重要,那麼此時中斷處理常式的執行時間應該是越短越好;
2、中斷處理常式正在執行時,會屏蔽同條中斷線上的插斷要求;而更嚴重的是,如果設定了IRQF_DISABLED,那麼該中斷服務程式執行時會屏蔽所有其他的插斷要求。那麼此時應該讓中斷處理常式執行的越快越好。
上半部:一個快速、非同步而簡單的處理常式專門來負責對硬體的插斷要求做出快速響應,與此同時也要完成那麼些對時間要求很嚴格的操作;
下半部:那麼對時間要求相對寬鬆,其他的剩餘工作會在稍後的任意時間執行。
下面是對上半部和下半部的工作劃分:
如果一個任務對時間非常敏感,將其放在中斷處理常式中執行;
如果一個任務中和硬體相關,將其放在中斷處理常式中執行;
如果一個任務要保證不被其他中斷(特別是相同的中斷)打斷,將其放在中斷處理常式執行;
其他所有任務,都應考慮放在下半部執行。
[下半部機制]
非強制中斷
非強制中斷保留給系統中對時間要求最嚴格以及最重要的下半部使用,目前只有兩個子系統(網路和scsi)直接使用非強制中斷。
Tasklet
Tasklet是利用非強制中斷實現的一種下半部機制。在選擇使用非強制中斷還是tasklet時,建議使用tasklet,除了網路和SCSI情況。相比非強制中斷,tasklet的介面更簡單,鎖保護要求較低。
工作隊列
工作隊列是可以把工作推後交由一個核心線程去執行,下半部總是會在進程上下文中執行,允許重新調度和睡眠。
三種下半部機制對比
下半部 |
上下文 |
順序執行保障 |
非強制中斷 |
中斷 |
沒有 |
Tasklet |
中斷 |
同類型不能同時執行 |
工作隊列 |
進程 |
沒有(和進程上下文一樣被調度) |