最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Linux內(nèi)核中的軟中斷、tasklet和工作隊列詳解(超詳細(xì)~)

2022-06-14 14:18 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

本文基于Linux2.6.32內(nèi)核版本。


引言

  • 軟中斷、tasklet和工作隊列并不是Linux內(nèi)核中一直存在的機(jī)制,而是由更早版本的內(nèi)核中的“下半部”(bottom half)演變而來。下半部的機(jī)制實際上包括五種,但2.6版本的內(nèi)核中,下半部和任務(wù)隊列的函數(shù)都消失了,只剩下了前三者。

  • 介紹這三種下半部實現(xiàn)之前,有必要說一下上半部與下半部的區(qū)別。

  • 上半部指的是中斷處理程序,下半部則指的是一些雖然與中斷有相關(guān)性但是可以延后執(zhí)行的任務(wù)。舉個例子:在網(wǎng)絡(luò)傳輸中,網(wǎng)卡接收到數(shù)據(jù)包這個事件不一定需要馬上被處理,適合用下半部去實現(xiàn);但是用戶敲擊鍵盤這樣的事件就必須馬上被響應(yīng),應(yīng)該用中斷實現(xiàn)。

  • 兩者的主要區(qū)別在于:中斷不能被相同類型的中斷打斷,而下半部依然可以被中斷打斷;中斷對于時間非常敏感,而下半部基本上都是一些可以延遲的工作。由于二者的這種區(qū)別,所以對于一個工作是放在上半部還是放在下半部去執(zhí)行,可以參考下面4條:

  1. 如果一個任務(wù)對時間非常敏感,將其放在中斷處理程序中執(zhí)行。

  2. 如果一個任務(wù)和硬件相關(guān),將其放在中斷處理程序中執(zhí)行。

  3. 如果一個任務(wù)要保證不被其他中斷(特別是相同的中斷)打斷,將其放在中斷處理程序中執(zhí)行。

  4. 其他所有任務(wù),考慮放在下半部去執(zhí)行。

  5. 有寫內(nèi)核任務(wù)需要延后執(zhí)行,因此才有的下半部,進(jìn)而實現(xiàn)了三種實現(xiàn)下半部的方法。這就是本文要討論的軟中斷、tasklet和工作隊列。

  • 下表可以更直觀的看到它們之間的關(guān)系。


軟中斷

  • 軟中斷作為下半部機(jī)制的代表,是隨著SMP(share memory processor)的出現(xiàn)應(yīng)運(yùn)而生的,它也是tasklet實現(xiàn)的基礎(chǔ)(tasklet實際上只是在軟中斷的基礎(chǔ)上添加了一定的機(jī)制)。軟中斷一般是“可延遲函數(shù)”的總稱,有時候也包括了tasklet(請讀者在遇到的時候根據(jù)上下文推斷是否包含tasklet)。它的出現(xiàn)就是因為要滿足上面所提出的上半部和下半部的區(qū)別,使得對時間不敏感的任務(wù)延后執(zhí)行,而且可以在多個CPU上并行執(zhí)行,使得總的系統(tǒng)效率可以更高。它的特性包括:

  • 產(chǎn)生后并不是馬上可以執(zhí)行,必須要等待內(nèi)核的調(diào)度才能執(zhí)行。軟中斷不能被自己打斷(即單個cpu上軟中斷不能嵌套執(zhí)行),只能被硬件中斷打斷(上半部)。

  • 可以并發(fā)運(yùn)行在多個CPU上(即使同一類型的也可以)。所以軟中斷必須設(shè)計為可重入的函數(shù)(允許多個CPU同時操作),因此也需要使用自旋鎖來保其數(shù)據(jù)結(jié)構(gòu)。

相關(guān)數(shù)據(jù)結(jié)構(gòu)

  • 軟中斷描述符

struct softirq_action{ void (*action)(struct softirq_action *);}; 描述每一種類型的軟中斷,其中void(*action)是軟中斷觸發(fā)時的執(zhí)行函數(shù)。

  • 軟中斷全局?jǐn)?shù)據(jù)和類型

相關(guān)API

  • 注冊軟中斷

  • 即注冊對應(yīng)類型的處理函數(shù)到全局?jǐn)?shù)組softirq_vec中。例如網(wǎng)絡(luò)發(fā)包對應(yīng)類型為NET_TX_SOFTIRQ的處理函數(shù)net_tx_action.

  • 觸發(fā)軟中斷

  • 實際上即以軟中斷類型nr作為偏移量置位每cpu變量irq_stat[cpu_id]的成員變量__softirq_pending,這也是同一類型軟中斷可以在多個cpu上并行運(yùn)行的根本原因。

  • 軟中斷執(zhí)行函數(shù)

  • 執(zhí)行軟中斷處理函數(shù)__do_softirq前首先要滿足兩個條件:

  1. 不在中斷中(硬中斷、軟中斷和NMI) 。

  2. 有軟中斷處于pending狀態(tài)。

  • 系統(tǒng)這么設(shè)計是為了避免軟件中斷在中斷嵌套中被調(diào)用,并且達(dá)到在單個CPU上軟件中斷不能被重入的目的。對于ARM架構(gòu)的CPU不存在中斷嵌套中調(diào)用軟件中斷的問題,因為ARM架構(gòu)的CPU在處理硬件中斷的過程中是關(guān)閉掉中斷的。只有在進(jìn)入了軟中斷處理過程中之后才會開啟硬件中斷,如果在軟件中斷處理過程中有硬件中斷嵌套,也不會再次調(diào)用軟中斷,because硬件中斷是軟件中斷處理過程中再次進(jìn)入的,此時preempt_count已經(jīng)記錄了軟件中斷!對于其它架構(gòu)的CPU,有可能在觸發(fā)調(diào)用軟件中斷前,也就是還在處理硬件中斷的時候,就已經(jīng)開啟了硬件中斷,可能會發(fā)生中斷嵌套,在中斷嵌套中是不允許調(diào)用軟件中斷處理的。Why?我的理解是,在發(fā)生中斷嵌套的時候,表明這個時候是系統(tǒng)突發(fā)繁忙的時候,內(nèi)核第一要務(wù)就是趕緊把中斷中的事情處理完成,退出中斷嵌套。避免多次嵌套,哪里有時間處理軟件中斷,所以把軟件中斷推遲到了所有中斷處理完成的時候才能觸發(fā)軟件中斷。

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ? ??

實現(xiàn)原理和實例

軟中斷的調(diào)度時機(jī):

  1. do_irq完成I/O中斷時調(diào)用irq_exit。

  2. 系統(tǒng)使用I/O APIC,在處理完本地時鐘中斷時。

  3. local_bh_enable,即開啟本地軟中斷時。

  4. SMP系統(tǒng)中,cpu處理完被CALL_FUNCTION_VECTOR處理器間中斷所觸發(fā)的函數(shù)時。

  5. ksoftirqd/n線程被喚醒時。

? ?下面以從中斷處理返回函數(shù)irq_exit中調(diào)用軟中斷為例詳細(xì)說明。

? ? 觸發(fā)和初始化的的流程如圖所示:

軟中斷處理流程

  1. 首先調(diào)用local_softirq_pending函數(shù)取得目前有哪些位存在軟件中斷。

  2. 調(diào)用__local_bh_disable關(guān)閉軟中斷,其實就是設(shè)置正在處理軟件中斷標(biāo)記,在同一個CPU上使得不能重入__do_softirq函數(shù)。

  3. 重新設(shè)置軟中斷標(biāo)記為0,set_softirq_pending重新設(shè)置軟中斷標(biāo)記為0,這樣在之后重新開啟中斷之后硬件中斷中又可以設(shè)置軟件中斷位。

  4. 調(diào)用local_irq_enable,開啟硬件中斷。

  5. 之后在一個循環(huán)中,遍歷pending標(biāo)志的每一位,如果這一位設(shè)置就會調(diào)用軟件中斷的處理函數(shù)。在這個過程中硬件中斷是開啟的,隨時可以打斷軟件中斷。這樣保證硬件中斷不會丟失。

  6. 之后關(guān)閉硬件中斷(local_irq_disable),查看是否又有軟件中斷處于pending狀態(tài),如果是,并且在本次調(diào)用__do_softirq函數(shù)過程中沒有累計重復(fù)進(jìn)入軟件中斷處理的次數(shù)超過max_restart=10次,就可以重新調(diào)用軟件中斷處理。如果超過了10次,就調(diào)用wakeup_softirqd()喚醒內(nèi)核的一個進(jìn)程來處理軟件中斷。設(shè)立10次的限制,也是為了避免影響系統(tǒng)響應(yīng)時間。

  7. 調(diào)用_local_bh_enable開啟軟中斷。

軟中斷內(nèi)核線程

  • 之前我們分析的觸發(fā)軟件中斷的位置其實是中斷上下文中,而在軟中斷的內(nèi)核線程中實際已經(jīng)是進(jìn)程的上下文。

  • 這里說的軟中斷上下文指的就是系統(tǒng)為每個CPU建立的ksoftirqd進(jìn)程。

  • 軟中斷的內(nèi)核進(jìn)程中主要有兩個大循環(huán),外層的循環(huán)處理有軟件中斷就處理,沒有軟件中斷就休眠。內(nèi)層的循環(huán)處理軟件中斷,每循環(huán)一次都試探一次是否過長時間占據(jù)了CPU,需要調(diào)度就釋放CPU給其它進(jìn)程。具體的操作在注釋中做了解釋。

tasklet

  • 由于軟中斷必須使用可重入函數(shù),這就導(dǎo)致設(shè)計上的復(fù)雜度變高,作為設(shè)備驅(qū)動程序的開發(fā)者來說,增加了負(fù)擔(dān)。而如果某種應(yīng)用并不需要在多個CPU上并行執(zhí)行,那么軟中斷其實是沒有必要的。因此誕生了彌補(bǔ)以上兩個要求的tasklet。它具有以下特性:

  1. 一種特定類型的tasklet只能運(yùn)行在一個CPU上,不能并行,只能串行執(zhí)行。

  2. 多個不同類型的tasklet可以并行在多個CPU上。

  3. 軟中斷是靜態(tài)分配的,在內(nèi)核編譯好之后,就不能改變。但tasklet就靈活許多,可以在運(yùn)行時改變(比如添加模塊時)。

  • tasklet是在兩種軟中斷類型的基礎(chǔ)上實現(xiàn)的,因此如果不需要軟中斷的并行特性,tasklet就是最好的選擇。也就是說tasklet是軟中斷的一種特殊用法,即延遲情況下的串行執(zhí)行。

相關(guān)數(shù)據(jù)結(jié)構(gòu)

  • tasklet描述符

tasklet鏈表

相關(guān)API

  • 定義tasklet

tasklet操作

實現(xiàn)原理

  • 調(diào)度原理

tasklet執(zhí)行過程 TASKLET_SOFTIRQ對應(yīng)執(zhí)行函數(shù)為tasklet_action,HI_SOFTIRQ為tasklet_hi_action,以tasklet_action為例說明,tasklet_hi_action大同小異。



工作隊列

  • 從上面的介紹看以看出,軟中斷運(yùn)行在中斷上下文中,因此不能阻塞和睡眠,而tasklet使用軟中斷實現(xiàn),當(dāng)然也不能阻塞和睡眠。但如果某延遲處理函數(shù)需要睡眠或者阻塞呢?沒關(guān)系工作隊列就可以如您所愿了。

  • 把推后執(zhí)行的任務(wù)叫做工作(work),描述它的數(shù)據(jù)結(jié)構(gòu)為work_struct ,這些工作以隊列結(jié)構(gòu)組織成工作隊列(workqueue),其數(shù)據(jù)結(jié)構(gòu)為workqueue_struct ,而工作線程就是負(fù)責(zé)執(zhí)行工作隊列中的工作。系統(tǒng)默認(rèn)的工作者線程為events。

  • 工作隊列(work queue)是另外一種將工作推后執(zhí)行的形式。工作隊列可以把工作推后,交由一個內(nèi)核線程去執(zhí)行—這個下半部分總是會在進(jìn)程上下文執(zhí)行,但由于是內(nèi)核線程,其不能訪問用戶空間。最重要特點的就是工作隊列允許重新調(diào)度甚至是睡眠。

  • 通常,在工作隊列和軟中斷/tasklet中作出選擇非常容易??墒褂靡韵乱?guī)則:

  1. 如果推后執(zhí)行的任務(wù)需要睡眠,那么只能選擇工作隊列。

  2. 如果推后執(zhí)行的任務(wù)需要延時指定的時間再觸發(fā),那么使用工作隊列,因為其可以利用timer延時(內(nèi)核定時器實現(xiàn))。

  3. 如果推后執(zhí)行的任務(wù)需要在一個tick之內(nèi)處理,則使用軟中斷或tasklet,因為其可以搶占普通進(jìn)程和內(nèi)核線程,同時不可睡眠。

  4. 如果推后執(zhí)行的任務(wù)對延遲的時間沒有任何要求,則使用工作隊列,此時通常為無關(guān)緊要的任務(wù)。

  • 實際上,工作隊列的本質(zhì)就是將工作交給內(nèi)核線程處理,因此其可以用內(nèi)核線程替換。但是內(nèi)核線程的創(chuàng)建和銷毀對編程者的要求較高,而工作隊列實現(xiàn)了內(nèi)核線程的封裝,不易出錯,所以我們也推薦使用工作隊列。

相關(guān)數(shù)據(jù)結(jié)構(gòu)

  • 正常工作結(jié)構(gòu)體

延遲工作結(jié)構(gòu)體(延遲的實現(xiàn)是在調(diào)度時延遲插入相應(yīng)的工作隊列)

每cpu工作隊列(每cpu都對應(yīng)一個工作者線程worker_thread)

相關(guān)API

  • 缺省工作隊列

  • 以上均是采用缺省工作者線程來實現(xiàn)工作隊列,其優(yōu)點是簡單易用,缺點是如果缺省工作隊列負(fù)載太重,執(zhí)行效率會很低,這就需要我們創(chuàng)建自己的工作者線程和工作隊列。

  • 自定義工作隊列

實現(xiàn)原理

1.工作隊列的組織結(jié)構(gòu)

  • 即workqueue_struct、cpu_workqueue_struct與work_struct的關(guān)系。

  • 一個工作隊列對應(yīng)一個work_queue_struct,工作隊列中每cpu的工作隊列由cpu_workqueue_struct表示,而work_struct為其上的具體工作。


  • 關(guān)系如下圖所示:


2.工作隊列的工作過程


應(yīng)用實例

  • linux各個接口的狀態(tài)(up/down)的消息需要通知netdev_chain上感興趣的模塊同時上報用戶空間消息。這里使用的就是工作隊列。

  • 具體流程圖如下所示:


  • 是否處于中斷中在Linux中是通過preempt_count來判斷的,具體如下: 在linux系統(tǒng)的進(jìn)程數(shù)據(jù)結(jié)構(gòu)里,有這么一個數(shù)據(jù)結(jié)構(gòu):

#define preempt_count() (current_thread_info()->preempt_count) 利用preempt_count可以表示是否處于中斷處理或者軟件中斷處理過程中,如下所示: # define hardirq_count() (preempt_count() & HARDIRQ_MASK) #define softirq_count() (preempt_count() & SOFTIRQ_MASK) #define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK)) #define in_irq() (hardirq_count()) #define in_softirq() (softirq_count()) #define in_interrupt() (irq_count())


  • preempt_count的8~23位記錄中斷處理和軟件中斷處理過程的計數(shù)。如果有計數(shù),表示系統(tǒng)在硬件中斷或者軟件中斷處理過程中。



Linux內(nèi)核中的軟中斷、tasklet和工作隊列詳解(超詳細(xì)~)的評論 (共 條)

分享到微博請遵守國家法律
高邮市| 广饶县| 芜湖市| 祁东县| 高雄县| 兴仁县| 平安县| 宿迁市| 翼城县| 阿克| 靖西县| 乌什县| 扎赉特旗| 谢通门县| 弋阳县| 高邮市| 朝阳区| 枣强县| 呈贡县| 龙泉市| 马龙县| 盈江县| 临潭县| 辰溪县| 堆龙德庆县| 武邑县| 连平县| 福建省| 即墨市| 烟台市| 阿坝| 长泰县| 黄陵县| 区。| 扎囊县| 车致| 淮阳县| 丰顺县| 米脂县| 辽阳市| 大兴区|