翻譯自:Software interrupts and realtimehtml
Linux內核的軟中斷("softirq")機制有些奇怪,在早期的Linux和處理機制下比較晦澀,且僅有極少的內核開發人員會直接接觸軟中斷。然而它是內核的大多數重要處理的核心。在某些場景下,軟中斷會以一種不合時宜的方式出現。特別是內核的實時搶佔補丁集常常會與軟中斷產生衝突,該補丁集的最新版本提供了一種解決產生軟中斷問題的方法,值得一看。linux
在3.6.1-rt1補丁集的聲明中,Thomas Gleixner使用以下方式描述了軟中斷:shell
首先,它是大部分不相關任務聚合的產物,運行在隨機對任務施加/解除控制的上下文中緩存
First of all, it's a conglomerate of mostly unrelated jobs, which run in the context of a randomly chosen victim w/o the ability to put any control on them.網絡
軟中斷處理幾乎(但不等同)與硬中斷同樣重要。軟中斷的優先級比較高(但也有例外,見下文),但低於硬中斷,所以會搶佔除硬中斷外的任何任務。dom
在很早之前,Linux存在32個硬中斷向量,併爲每一個向量分配一個設備驅動或相關的任務。大部分驅動在很早之前就已經跟軟中斷分進行了分離(驅動仍然會使用軟中斷,但須要經過中間APIs,如tasklets和timers)。當前內核中有10種軟中斷向量:2種用於微線程(tasklet)處理,2種用於網絡,2種用於塊層(block layer,塊設備使用的),2種用於定時器,調度器和read-copy-update(RCU)處理各使用了一種。內核經過CPU位掩碼來指定須要處理(任意時間可能發生的)軟中斷的CPU。例如,當一個內核子系統調用tasklet_schedule()
時,會在對應的CPU上設置TASKLET_SOFTIRQ
比特位,當軟中斷處理完畢後(開中斷),會運行微線程 (tasklet基於軟中斷)。ide
# cat /proc/softirqs CPU0 CPU1 HI: 1 0 //高優先級的tasklet TIMER: 104838818 108267618 //基於系統tick的定時器 NET_TX: 2 1 //數據發送 NET_RX: 11622033 2698 //數據接收 BLOCK: 37 6833945 //塊設備訪問 BLOCK_IOPOLL: 0 0 // TASKLET: 9 46 //普通優先級的tasklet SCHED: 61485884 65788587 //多CPU調度 HRTIMER: 0 0 //高精度定時器 RCU: 48876416 46889277
有兩種狀況會引起軟中斷並搶佔當前線程:一種是在處理完一個硬中斷時,中斷處理程序會觸發軟中斷(硬中斷以後會觸發軟中斷,用於處理硬中斷的信號或數據,如網卡報文等),爲了某些目的(如減少延遲,優化緩存等) 須要儘快處理該軟中斷,這樣就可以從新啓用硬中斷;另外一種是內核代碼(在任什麼時候候)可能會(經過調用如local_bh_enable()
或spin_unlock_bh()
函數,這兩個函數用於中斷保護,防止其餘中斷混入處理,相似鎖機制)從新啓用軟中斷,這樣會致使積累的軟中斷在任意一個進程的上下文中運行,該進程也就是Thomas 所說的"隨機挑選的犧牲品"("randomly chosen victim")。函數
讀者可能會對系統上運行的ksoftirqd
感到疑惑,該進程主要用於在系統的軟中斷負載太高時下降軟中斷的處理。正規處理中,若是內聯的軟中斷進程代碼在循環處理10次以後,發現還須要處理更多的軟中斷(因爲不斷產生中斷),此時中斷進程會喚醒合適的ksoftirqd
(每一個CPU都有一個ksoftirqd
進程)進程並退出,後續由ksoftirqd
進程處理軟中斷。Ksoftirqd
能夠被(硬件或軟件)中斷上下文以外的軟中斷打斷,這種處理是必要的,不然Ksoftirqd
在處理下一個軟中斷前能夠運行任意時間。在老的內核中,Ksoftirqd
進程以最低的優先級運行,即對軟中斷的處理取決於該進程是系統上的最高優先級仍是最低優先級。從2.6.23開始,Ksoftirqd
默認使用普通用戶優先級運行。性能
在通常的系統上,軟中斷機制已經足夠處理大部分狀況,也不須要作過多改進。然而如The new visibility of RCU processing中描述,在3.7內核中, read-copy-update的任務已經移到其輔助線程中。在實時處理中,強制任意的進程作一些隨機工做的方式並不受歡迎,傳統的實時補丁會將全部的軟中斷隔離到獨立的線程中,每一個線程都有各自的優先級。在這樣的處理下,如,當網絡須要實時響應時,該中斷處理的線程的優先級會提升;相反地,當網絡事件不那麼緊急時,線程的優先級會下降。優化
從3.0實時補丁集開始,上面的處理方式沒法繼續工做。如它沒法與Per-CPU variables and the realtime tree很好地配合使用,正如Thomas所說, 採用軟中斷線程的方式會致使配置問題:
一般很難從一個實時系統中得到合適的參數。將一些像軟中斷同樣晦澀的工做添加到系統設計人員的待作事項例並非個好主意。
It's extremely hard to get the parameters right for a RT system in general. Adding something which is obscure as soft interrupts to the system designers todo list is a bad idea.
所以,從3.0開始,軟中斷的處理與主線內核的處理很是相似。使用這種方式改進了代碼質量並提高了非協調系統(經過消除切換到軟中斷線程的上下文)的性能,但也剝奪了傾向於對這種方式進行細微調整的能力(一些專一實時的開發者很是傾向於使用這種方式)。這樣也是一些用戶對這種改變抱怨的地方。
做爲迴應,3.6.1-rt1對軟中斷的處理又做了改動。如今,當一個線程觸發一個軟中斷時,內核會保存特定的中斷(如處理接收到的網絡報文時)。一旦線程退出,內核會禁用該軟中斷的上下文,並運行下一個軟中斷,使用這種方式能夠減少處理軟中斷的延遲(因爲會當即運行下一個軟中斷)。一樣重要的是,這種方式將軟中斷和產生該軟中斷的進程綁定到了一塊兒。這樣產生網絡軟中斷的進程不會陷入處理其餘進程的定時器的困境中,使得軟中斷處理本地化,消除因爲處理其餘進程的軟中斷形成的不肯定性,並使得軟中斷可以以一開始建立任務的進程的優先級運行。
但有一種例外:由硬中斷引起的軟中斷不能使用這種方式處理。因爲沒法將硬中斷與一個特定的線程進行關聯,所以不能使用對應的線程作必要的處理。這種狀況下會將這些軟中斷交給ksoftirqd
進程處理。
Thomas暗示的下一步處理邏輯爲,將一個禁用全部軟中斷的環境轉變爲僅禁用特定軟中斷的環境。大多數禁用軟中斷的代碼僅關心某一特定的軟中斷處理,其餘都容許正常運行。更進一步,Thomas補充到"最好的方式是徹底擺脫軟中斷"。移除軟中斷機制已經在"待作"列表中存在了很長時間,但沒有人實際作這些工做。
實時補丁集的性質使得用戶對主線內核的缺陷感到痛苦,這致使來自實時社區的大量主線代碼修改和提高。目前,實時用戶已經有了一個改進的軟中斷機制,使其沒必要再進行底層調優。