軟中斷介紹
把可以延遲的處理從硬中斷處理程序獨(dú)立出來(lái),,這樣這個(gè)處理可以在開(kāi)中斷的情況下運(yùn)行,這個(gè)處理就是軟中斷,??梢?jiàn),軟中斷的這種脫離可以大大縮短硬中斷的響應(yīng)時(shí)間,,對(duì)于很多實(shí)時(shí)應(yīng)用來(lái)說(shuō)及其重要,。
我們本文只談軟中斷,至于tasklet,、workqueue等我們以后再談,。我們?cè)谥v述軟中斷流程(參考linux kernel 4.0)時(shí)會(huì)嘗試深入理解其中的各個(gè)細(xì)節(jié)之處,分享我們自己的理解(如果不正,,還望指出,,謝謝),。
(題圖來(lái)自:techvark.com)
軟中斷數(shù)據(jù)結(jié)構(gòu)的定義
軟中斷目前有10(由NR_SOFTIRQS定義)個(gè),,通過(guò)softirq_vec[NR_SOFTIRQS]數(shù)組來(lái)管理這些軟中斷,全部cpu共用,。
軟中斷的注冊(cè)
通過(guò)open_softirq()將具體的軟中斷處理函數(shù)和軟中斷編號(hào)綁定,。如網(wǎng)絡(luò)系統(tǒng)注冊(cè)了收發(fā)包的軟中斷處理函數(shù): open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action);
軟中斷的激活
每個(gè)cpu都有一個(gè)32bit的位圖(即__softirq_pending)來(lái)維護(hù)本cpu上的軟中斷是否激活。 typedef struct { unsigned int __softirq_pending; #ifdef CONFIG_SMP unsigned int ipi_irqs[NR_IPI]; #endif } ____cacheline_aligned irq_cpustat_
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;
軟中斷的激活時(shí)機(jī)之一:irq_exit
irq_exit函數(shù)里可能會(huì)激活軟中斷,,激活條件是:
不在硬中斷里并且不在軟中斷里并且本cpu的__softirq_pending中有置位,。 if (!in_interrupt() && local_softirq_pending()) invoke_softirq();
由這個(gè)條件,我們可以知道,,軟中斷和硬中斷在這里是同等對(duì)待(在in_interrupt里)的,,體現(xiàn)都是中斷處理這一個(gè)本質(zhì)。不能在硬中斷里的條件,,表明必須優(yōu)先性,,必須硬中斷全部處理完,才考慮軟中斷,;不能在軟中斷里的條件,,表明屏蔽了軟中斷的嵌套,。
invoke_softirq函數(shù)的處理是,要么(先喚醒ksoftirqd)將軟中斷交由ksoftirqd專(zhuān)門(mén)線程處理,,要么直接調(diào)用__do_softirq即時(shí)處理(當(dāng)然,,即時(shí)處理要區(qū)分是在哪個(gè)棧上:是當(dāng)前棧上還是在獨(dú)立的軟中斷棧上)。
我們看看即時(shí)處理這個(gè)流程,。local_softirq_pending前肯定會(huì)清除preempt_count中的硬中斷位,,如果此時(shí)preempt_count里沒(méi)有軟中斷位則可以被搶占(即時(shí)關(guān)閉硬中斷)。在進(jìn)入到__do_softirq處理各個(gè)軟中斷期間,,肯定是禁止搶占了,。在硬(軟)中斷上下文里的搶占是眾所周知不被允許的:會(huì)讓被中斷的進(jìn)程執(zhí)行時(shí)間不確定,也是不公平的(也就是說(shuō),,不要在硬中斷和軟中斷的處理中有調(diào)度離開(kāi)的意向),。
軟中斷的激活時(shí)機(jī)之二:raise_softirq
網(wǎng)卡收包方式從非NAPI進(jìn)化到NAPI方式,就充分展示了軟中斷的優(yōu)點(diǎn):把收?qǐng)?bào)任務(wù)最大程度地交給軟中斷處理,,最大程度簡(jiǎn)化硬中斷處理,。這種進(jìn)化,我們以后再講,。
raise_softirq函數(shù)會(huì)調(diào)用__raise_softirq_irqoff函數(shù),,在指定cpu的__softirq_pending位圖上置位相應(yīng)的軟中斷。raise_softirq_irqoff函數(shù)和raise_softirq函數(shù)的區(qū)別是關(guān)中斷的操作是否已經(jīng)完成了,。置位位圖是一個(gè)競(jìng)爭(zhēng)操作,,所有硬中斷里都可能做,所以得保證在關(guān)中斷的情況下完成,。
軟中斷的激活之三:ksoftirqd
每個(gè)cpu都有一個(gè)ksoftirqd線程在軟中斷量大時(shí)專(zhuān)門(mén)處理軟中斷: DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
ksoftirqd線程的核心函數(shù)run_ksoftirqd的(循環(huán))處理是:關(guān)中斷看本cpu的__softirq_pending的置位情況,,如有則執(zhí)行__do_softirqd(),執(zhí)行完開(kāi)中斷),。這個(gè)執(zhí)行很順暢,,因?yàn)槭窃谠摼€程自己的棧上,不會(huì)有影響用戶(hù)進(jìn)程的問(wèn)題,。
這里有個(gè)疑問(wèn),,此處以前是關(guān)搶占保護(hù),現(xiàn)在是關(guān)中斷的保護(hù)了(參考2012年的patch 3e339b,,softirq: Use hotplugthread infrastructure),?我們的理解是:關(guān)搶占的保護(hù)方式,會(huì)讓后續(xù)更多的軟中斷由ksoftirqd處理,,不符合ksoftirqd的輔助地位,。就處理軟中斷的地位而言,應(yīng)該是irq_exit的為主,ksoftirqd的為輔,。)
ksoftirqd里也可以看到,,在執(zhí)行軟中斷前是可以被搶占的,但是一旦開(kāi)始執(zhí)行就不能被搶占了(和上面的調(diào)度之一:irq_exit中的講述的思想是一致的),。就是說(shuō),,軟中斷和硬中斷的處理思想是一致的:執(zhí)行期間不允許發(fā)生調(diào)度!
上述不能搶占的原因其實(shí)就是類(lèi)似事務(wù)性的一個(gè)原則:一旦開(kāi)始不能停止,。另外一個(gè)原因是,,執(zhí)行的是用戶(hù)自定義的硬(軟)中斷程序,操作具有不確定性,,如果讓這些操作期間具有調(diào)度可能,,則會(huì)脫離內(nèi)核的控制范圍。
軟中斷的激活之四:其他地方
比如netif_rx_ni(),,執(zhí)行do_softirq前關(guān)搶占,,不能在執(zhí)行軟中斷期間調(diào)度。
軟中斷的激活之五:local_bh_enableif (unlikely(!in_interrupt() && local_softirq_pending())) do_softirq();
想想,,如果異常和軟中斷有共享數(shù)據(jù)的話,,異常處理走到此共享數(shù)據(jù)的臨界區(qū)時(shí)需要關(guān)軟中斷,但不需要關(guān)硬中斷,。那么當(dāng)走完臨界區(qū)時(shí),,需要開(kāi)軟中斷,此時(shí)就是一個(gè)激活時(shí)機(jī)(看preempt_count了,,其實(shí)可能也是一個(gè)搶占時(shí)機(jī)),。
用“激活”而不是“調(diào)用”的原因是外圍處理僅修改本cpu的__softirq_pending位圖,最后由核心機(jī)制(比如ksoftirqd,、能通過(guò)in_interrupt檢查的軟中斷處理)真正處理,,而這就是軟中斷的理念:讓硬中斷(或者其它)更快執(zhí)行,所以不會(huì)采用直接調(diào)用的方式,。
“激活”的原則是誰(shuí)激活,,誰(shuí)處理,哪個(gè)cpu上的硬中斷帶來(lái)的軟中斷就由哪個(gè)cpu處理(或者說(shuō),,歸屬cpu是軟中斷跟著硬中斷走)。這樣,,充分發(fā)揮smp的優(yōu)勢(shì),,均衡到各個(gè)cpu上。至于硬中斷和cpu之間的關(guān)系,,我們以后講到硬中斷時(shí)再討論,。每個(gè)cpu維護(hù)自己的軟中斷機(jī)制就行了,各個(gè)cpu是互不相關(guān)的,。注意,,還是有相關(guān)性的:各個(gè)cpu并行處理同一類(lèi)型的軟中斷時(shí),,該類(lèi)型軟中斷處理需要為共享數(shù)據(jù)做保護(hù),這是軟中斷可重入性需要付出的代價(jià),。
軟中斷核心函數(shù)處理之do_softirq
do_softirq先檢查軟中斷重入條件:必須不在硬中斷里并且不在軟中斷里,,符合條件之后就可以開(kāi)始做如下的軟中斷處理了: pending = local_softirq_pending(); if (pending) __do_softirq();
這個(gè)處理是在關(guān)中斷的保護(hù)下完成的,畢竟軟中斷和硬中斷本質(zhì)上是一樣的,,都是中斷體系的(當(dāng)然,,進(jìn)入到硬/軟中斷內(nèi)部再開(kāi)則另當(dāng)別論了)。也可以看到,,局部變量pending沒(méi)有傳入__do_softirq內(nèi)部,,所以此處僅是判斷,不是使用,,此處判斷值和內(nèi)部使用值可能有差異,,位圖中置位位數(shù)會(huì)少一些。
我們?cè)偕罹恳幌逻@個(gè)檢查條件,。我們的理解是:
這個(gè)條件達(dá)到了兩個(gè)效果:同一個(gè)cpu上的軟中斷不嵌套,;嵌套硬中斷中不處理軟中斷。就同一個(gè)cpu而言,,__do_softirq函數(shù)的執(zhí)行是串行的,,非重入的(do_softirq函數(shù)可以說(shuō)是可重入的);就多個(gè)cpu而言,,__do_softirq函數(shù)是可重入的,,即使是同一個(gè)類(lèi)型的軟中斷。也就是說(shuō),,軟中斷通過(guò)這個(gè)檢查條件做到了本cpu上的軟中斷處理串行化,,當(dāng)然,多cpu之間的還是并行的,,所以同一類(lèi)型軟中斷處理還是需要保護(hù)自己的相關(guān)共享數(shù)據(jù)結(jié)構(gòu)的,。
軟中斷核心函數(shù)處理之__do_softirq
__do_softirq函數(shù)處理是盡量(雖然可能還是執(zhí)行不完)執(zhí)行所有被激活的軟中斷(由本cpu上的__softirq_pending位圖標(biāo)識(shí))處理。我們分三個(gè)階段分析,。
準(zhǔn)備處理階段:關(guān)閉軟中斷(效果是讓上面提到的檢查條件為真,,從而達(dá)到禁止本cpu上的軟中斷嵌套的目的)。
核心處理階段:關(guān)硬中斷,,獲得本cpu的__softirq_pending位圖并存儲(chǔ)起來(lái),,清空位圖,開(kāi)硬中斷(僅在讀寫(xiě)位圖時(shí)需要關(guān)硬中斷,,防止其它硬中斷同時(shí)操作),。執(zhí)行本cpu的所有軟中斷(由存儲(chǔ)起來(lái)的位圖獲得)。這個(gè)核心處理是個(gè)循環(huán),最多10次(MAX_SOFTIRQ_RESTART),,畢竟此時(shí)用的是用戶(hù)進(jìn)程的棧,,不能借用太久。退出循環(huán)的條件是:總時(shí)間超出或者被搶占(開(kāi)中斷就會(huì)有被搶占)或者達(dá)到10次了,。
結(jié)尾處理階段:關(guān)硬中斷,,開(kāi)軟中斷。
另外,,如果10次循環(huán)都解決不完軟中斷,,說(shuō)明期間發(fā)生的硬中斷很多,帶來(lái)的額外的軟中斷也很多,。那么就不繼續(xù)影響借用的用戶(hù)進(jìn)程棧了,,直接交給專(zhuān)門(mén)的ksoftirqd內(nèi)核線程處理。這也就說(shuō)明了循環(huán)的含義:處理軟中斷期間時(shí)還會(huì)進(jìn)入新的硬中斷,,從而帶進(jìn)新的軟中斷(當(dāng)然,,僅僅是在本cpu的__softirq_pending上置位,不會(huì)有實(shí)際處理),,所以需要反復(fù)去處理(處理的目標(biāo)很明確,,就是要清空本cpu上的__softirq_pending位圖)。
再看看那個(gè)防止軟中斷嵌套的流程,。關(guān)軟中斷中肯定有一句原子地加1的關(guān)鍵語(yǔ)句,,如果當(dāng)前內(nèi)核路徑A在該原子操作之前被另一個(gè)內(nèi)核路徑B打斷,則B執(zhí)行完硬中斷和軟中斷后,,返回到A的此處,,A接著執(zhí)行該原子操作,之后的軟中斷處理應(yīng)該是空轉(zhuǎn),,因?yàn)榭隙ㄒ呀?jīng)被B處理完了,。如果在該原子操作之后被B打斷,則B執(zhí)行完硬中斷,,不會(huì)執(zhí)行自己的軟中斷而是會(huì)直接退出(因?yàn)檐浿袛嗲短琢耍?,返回到A的此處,A接著執(zhí)行,,這次A除了處理自己軟中斷,,還會(huì)額外地處理B的軟中斷。
對(duì)于preempt_count中的軟中斷位,,由上述可以知道,,它的作用有兩個(gè):防止軟中斷在單cpu上嵌套;保證了在執(zhí)行軟中斷期間不被搶占,。
最后,還得重復(fù)一句:這里講的__do_softirq函數(shù)都是在一個(gè)cpu上的處理,多個(gè)cpu上的并行是不受任何控制的,。
總結(jié)
關(guān)于中斷的時(shí)序貌似很復(fù)雜,,但其實(shí)都逃不過(guò)兩個(gè)原則:硬中斷會(huì)打斷硬中斷(當(dāng)然是不同類(lèi)型的);硬中斷會(huì)打斷軟中斷(同樣地:軟中斷不會(huì)打斷硬中斷,,軟中斷也不會(huì)打斷軟中斷),。所有貌似復(fù)雜的時(shí)序其實(shí)都只是這兩個(gè)的疊加而已。
作者:計(jì)算機(jī)學(xué)習(xí)微信公眾號(hào)(jsj_xx)
|