Linux驅(qū)動(dòng)中斷下半部的三種方法

什么是中斷下半部

當(dāng)產(chǎn)生一個(gè)中斷時(shí),會(huì)進(jìn)入中斷處理程序。

中斷處理程序必須快速、異步、簡單的對(duì)硬件做出迅速響應(yīng)并完成那些時(shí)間要求很嚴(yán)格的操作。

因此,對(duì)于那些其他的、對(duì)時(shí)間要求相對(duì)寬松的任務(wù),就應(yīng)該推后到中斷被激活以后再去運(yùn)行

這樣,整個(gè)中斷處理流程就被分為了兩個(gè)部分

  • 第一個(gè)部分是中斷處理程序(上半部),內(nèi)核通過對(duì)它的異步執(zhí)行完成對(duì)硬件中斷的即時(shí)響應(yīng)。
  • 中斷處理流程中的另外那一部分,下半部(bottom half)

下半部的任務(wù)主要是執(zhí)行與中斷相關(guān)的工作,這些工作沒有被中斷服務(wù)程序本身完成

Linux驅(qū)動(dòng)中斷下半部的三種方法

下半部并不需要指明一個(gè)確切時(shí)間,只要把這些任務(wù)推遲一點(diǎn),讓它們在系統(tǒng)不太繁忙并且中斷恢復(fù)后執(zhí)行就可以了。

上半部和下半部的主要區(qū)別

  • 上半部指的是中斷處理程序下半部則指的是一些雖然與中斷有相關(guān)性但是可以延后執(zhí)行的任務(wù)。

  • 上半部中斷不能被相同類型的中斷打斷,而下半部依然可以被中斷打斷

  • 通常下半部在中斷處理程序一返回就會(huì)馬上運(yùn)行。

  • 上半部分簡單快速,執(zhí)行的時(shí)候禁止一些或者全部中斷。

  • 下半部分稍后執(zhí)行,而且執(zhí)行期間可以響應(yīng)所有的中斷。

linux中,對(duì)中斷下半部的實(shí)現(xiàn)主要有三種:

  • 軟中斷
  • tasklet
  • 工作隊(duì)列

softirq

softirq即軟中斷,代碼位于kernel/softirq.c文件中;

每個(gè)軟中斷由softirq_action結(jié)構(gòu)表示:

Linux驅(qū)動(dòng)中斷下半部的三種方法

在softirq.c中定義了一個(gè)軟中斷向量數(shù)組softirq_vec:

static?struct?softirq_action?softirq_vec[NR_SOFTIRQS]?__cacheline_aligned_in_smp;?? ????enum?? ????{?? ???????HI_SOFTIRQ=0,?/*用于高優(yōu)先級(jí)的tasklet*/?? ???????TIMER_SOFTIRQ,?/*用于定時(shí)器的下半部*/?? ???????NET_TX_SOFTIRQ,?/*用于網(wǎng)絡(luò)層發(fā)包*/?? ???????NET_RX_SOFTIRQ,?/*用于網(wǎng)絡(luò)層收?qǐng)?bào)*/?? ???????BLOCK_SOFTIRQ,?? ???????BLOCK_IOPOLL_SOFTIRQ,?? ???????TASKLET_SOFTIRQ,?/*用于低優(yōu)先級(jí)的tasklet*/?? ???????SCHED_SOFTIRQ,?? ???????HRTIMER_SOFTIRQ,?? ???????RCU_SOFTIRQ,?/*?Preferable?RCU?should?always?be?the?last?softirq?*/?? ???????NR_SOFTIRQS?? ???};

數(shù)組的成員數(shù)由NR_SOFTIRQS決定,是一個(gè)枚舉常量。

新增一個(gè)軟中斷時(shí),需要在文件include/linux/interrupt.h 中添加一個(gè)枚舉常量。

軟中斷使用的幾個(gè)要點(diǎn)

  • 一個(gè)軟中斷不會(huì)搶占另外一個(gè)軟中斷。
  • 惟一可以搶占軟中斷的是中斷處理程序。
  • 其他的軟中斷可以在其他處理器上同時(shí)執(zhí)行。

相關(guān)接口

  • 注冊軟中斷
void?open_softirq(int?nr,?void?(*action)(struct?softirq_action?*))

即注冊對(duì)應(yīng)類型的處理函數(shù)到全局?jǐn)?shù)組softirq_vec中。

  • 觸發(fā)軟中斷
void?raise_softirq(unsigned?int?nr)

實(shí)際上即以軟中斷類型nr作為偏移量會(huì)置位irq_stat[cpu_id]的成員變量__softirq_pending.

__softirq_pending字段中的每一個(gè)bit,對(duì)應(yīng)著某一個(gè)軟中斷,某個(gè)bit被置位,說明有相應(yīng)的軟中斷等待處理。

這也是同一類型軟中斷可以在多個(gè)cpu上并行運(yùn)行的根本原因。

軟中斷實(shí)例

以一個(gè)按鍵驅(qū)動(dòng)的中斷處理為例,將按鍵驅(qū)動(dòng)的中斷處理分成上下兩部分:

  • 上半部:讀取鍵值,觸發(fā)軟中斷
  • 下半部:喚醒進(jìn)程
Linux驅(qū)動(dòng)中斷下半部的三種方法

軟中斷的注冊,在驅(qū)動(dòng)的入口函數(shù),注冊軟中斷:

Linux驅(qū)動(dòng)中斷下半部的三種方法

添加的枚舉常量:

Linux驅(qū)動(dòng)中斷下半部的三種方法

可以看到,使用軟中斷是需要修改內(nèi)核,添加一個(gè)枚舉的,有些繁瑣。

所以,通常我們不建議擅自增加軟中斷的數(shù)量,如果需要新的軟中斷,盡可能把它們實(shí)現(xiàn)為基于軟中斷的tasklet形式。

tasklet

tasklet是利用軟中斷實(shí)現(xiàn)的一種下半部機(jī)制

那是用軟中斷還是tasklet好呢?

選擇到底是用軟中斷還是tasklet其實(shí)很簡單:

  • 通常你應(yīng)該用tasklet。就像我們在前面看到的,軟中斷資源有限,也麻煩,而且軟中斷的使用者屈指可數(shù)。它只在那些執(zhí)行頻率很高和連續(xù)性要求很高的情況下才需要。
  • tasklet卻有更廣泛的用途。大多數(shù)情況下用tasklet效果都不錯(cuò),而且它們還非常容易使用。
  • 因?yàn)?strong style="color: black;">tasklet是通過軟中斷實(shí)現(xiàn)的,所以它們本身也是軟中斷

tasklet使用

tasklet的使用步驟如下:

1、編寫tasklet處理函數(shù)(下半部

void?my_tasklet_fun?(unsigned?long?data)

2、聲明tasklet

//靜態(tài)? DECLARE_TASKLET(my_tasklet,my_tasklet_fun,data);? //動(dòng)態(tài) Struct??tasklet_struct?xxx; tasklet_init(&xxx,tasklet_handler,dev)

3、調(diào)度 tasklet

tasklet_schedule(&my_tasklet);

登記my_tasklet, 然后允許系統(tǒng)在合適的時(shí)間調(diào)度它。

tasklet實(shí)例

以按鍵中斷驅(qū)動(dòng)為例:

Linux驅(qū)動(dòng)中斷下半部的三種方法

先使用DECLARE_TASKLET靜態(tài)聲明一個(gè)tasklet,指定其下半部函數(shù)為btn_tasklet_func,在中斷服務(wù)函數(shù)(上半部)獲取按鍵值后,調(diào)用tasklet_schedule調(diào)度。

work queue

work queue即工作隊(duì)列,也是中斷下半部的一種。

Work queue將下半部工作推遲給一個(gè)內(nèi)核線程去執(zhí)行 ——work 總是運(yùn)行于進(jìn)程上下文.

兩個(gè)要點(diǎn)

  • 如果推遲的工作需要睡眠,則使用work queues。否則使用softirq或tasklets.
  • Work queues適用于需要分配大量的內(nèi)存,獲得一個(gè)信號(hào)量,或者執(zhí)行阻塞的I/O的情況.

工作隊(duì)列的相關(guān)接口函數(shù):

Linux驅(qū)動(dòng)中斷下半部的三種方法

在使用上,工作隊(duì)列與tasklet是類似的:

Linux驅(qū)動(dòng)中斷下半部的三種方法

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊13 分享