目錄
一、中斷
二、中斷處理程序
三、注冊中斷處理程序
四、卸載中斷處理程序
五、編寫中斷處理程序
六、中斷上下文
七、中斷下半部(bottom half)
軟中斷
Tasklet?
工作隊(duì)列
一、中斷
中斷使得硬件得以發(fā)出通知給處理器。中斷隨時都可以產(chǎn)生,如鍵盤敲擊就會觸發(fā)中斷,通知操作系統(tǒng)有按鍵按下。
不同設(shè)備對應(yīng)的中斷不同,而每個中斷都通過一個唯一的數(shù)字標(biāo)識。這些中斷值通常被稱為中斷請求(IRQ)線。每個 IRQ 線都會關(guān)聯(lián)一個數(shù)值量。
異常與中斷不同,它在產(chǎn)生時必須考慮與處理器時鐘同步,異常也常常被稱為同步中斷。在處理器執(zhí)行到錯誤指令時候(如除數(shù)為0),或者是在執(zhí)行期間出現(xiàn)特殊情況(如缺頁),這些異常需要通過內(nèi)核來處理,處理器就會產(chǎn)生一個異常。中斷還可以通過軟中斷實(shí)現(xiàn)系統(tǒng)調(diào)用。
二、中斷處理程序
在響應(yīng)一個特定中斷的時候,內(nèi)核會執(zhí)行一個函數(shù),該函數(shù)叫做中斷處理程序(interrupt handler)?或中斷服務(wù)例程(interrupt service routine,ISR)。每種類型的中斷都有一個相應(yīng)的中斷處理程序。一個設(shè)備的中斷處理程序是它設(shè)備驅(qū)動程序(driver)的一部分——設(shè)備驅(qū)動程序是用于對設(shè)備進(jìn)行管理的內(nèi)核代碼。
中斷處理程序與其他內(nèi)核函數(shù)的真正區(qū)別在于,中斷處理程序是被內(nèi)核調(diào)用來響應(yīng)中斷的,而它們運(yùn)行于我們稱之為中斷上下文的特殊上下文中。
中斷可能隨時發(fā)生,因此中斷處理程序也就隨時可能執(zhí)行。所以必須保證中斷處理程序能夠快速執(zhí)行,這樣才能保證盡可能快地恢復(fù)中斷代碼的執(zhí)行。
一般把中斷處理切位兩個部分:中斷處理程序是上半部(top half)——接收到一個中斷,上半部立刻開始執(zhí)行,但只做有嚴(yán)格時限的工作,例如對接收的中斷進(jìn)行應(yīng)答或復(fù)位硬件,這些工作都是在所有中斷被禁止的情況下完成的。能夠被允許稍后完成的工作會推遲到下半部(bottom half)去。
三、注冊中斷處理程序
中斷處理程序是管理硬件的驅(qū)動程序的組成部分。如果設(shè)備使用中斷,那么相應(yīng)的驅(qū)動程序就注冊一個中斷處理程序。
驅(qū)動程序可以通過 request_irq() 函數(shù)注冊一個中斷處理程序(聲明在 <linux/interrupt.h>),并且激活給定的中斷線,以處理中斷:
第一個參數(shù) irq 表示要分配的中斷號。
第二個參數(shù) handler 是一個指針,指向處理這個中斷的實(shí)際中斷處理程序。只要操作系統(tǒng)一接收到中斷,該函數(shù)就被調(diào)用。
注意 handler 函數(shù)的原型,它接受兩個參數(shù),并有一個類型為 irqreturn_t 的返回值。
第三個參數(shù) flags 可以為 0,也可能是下列一個或多個標(biāo)志的位掩碼。定義在 <linux/interrupt.h>。其中最重要的幾個標(biāo)志是:
- IRQF_DISABLED——該標(biāo)志被設(shè)置后,意味著內(nèi)核在處理中斷處理程序本身期間,要禁止所有的其他中斷。多數(shù)中斷處理程序是不會設(shè)置該位,這種用法留給希望快速執(zhí)行的輕量級中斷。
- IRQF_TIMER——該標(biāo)志是特別為系統(tǒng)定時器的中斷處理而準(zhǔn)備的。
- IRQF_SHARED——此標(biāo)志標(biāo)明可以在多個中斷處理程序之間共享中斷線。
第四個參數(shù) name 是與中斷相關(guān)的設(shè)備的 ASCII 文本表示
第五個參數(shù) dev 用于共享中斷線。當(dāng)一個中斷處理程序需要釋放時,dev 將提供唯一的標(biāo)志信息(cookie),以便從共享中斷線的諸多中斷處理程序中刪除指定的那一個。如果無需共享中斷線則設(shè)置為 NULL 即可。內(nèi)核每次調(diào)用中斷處理程序時,都會把這個指針傳遞給它。實(shí)踐中往往會通過它來傳遞驅(qū)動程序的設(shè)備結(jié)構(gòu)。
request_irq() 函數(shù)成功執(zhí)行會返回 0,非 0 值則代表有錯誤發(fā)生。
request_irq() 函數(shù)可能會睡眠,因此不能在中斷上下文或其他不允許阻塞的代碼中調(diào)用該函數(shù)。因?yàn)?kmalloc() 是可睡眠的。
四、卸載中斷處理程序
卸載驅(qū)動程序時,需要注銷相應(yīng)的中斷處理程序,并釋放中斷線。上述動作需要調(diào)用:
void free_irq(unsigned int irq, void *dev)
如果指定的中斷線不是共享的,則刪除處理程序的同時將禁用這條中斷線。如果中斷線是共享的,則刪除 dev 所對應(yīng)的處理程序,并不禁用中斷線。
五、編寫中斷處理程序
以下是一個中斷處理程序聲明:
static irqreturn_t intr_handler(int irq, void *dev)
中斷處理程序的返回值為?irqreturn_t。中斷處理程序可能返回兩個特殊的值:IRQ_NONE 和 IRQ_HANDLED。當(dāng)中斷處理程序檢測到一個中斷,但該中斷對應(yīng)的設(shè)備并不是在注冊處理函數(shù)期間指定的產(chǎn)生源的時候,返回?IRQ_NONE。反之則返回?IRQ_HANDLED。
Linux 的中斷處理程序是無需重入的。同一個中斷處理程序絕不會被同時調(diào)用以處理嵌套的中斷。
共享的處理程序的特點(diǎn)有:
- request_irq() 的參數(shù) flags 必須設(shè)置 IRQF_SHARED 標(biāo)志。
- 對于每個注冊的中斷處理程序來說,dev 參數(shù)必須唯一。指向任一設(shè)備結(jié)構(gòu)的指針就是唯一的。
- 中斷處理程序必須能夠區(qū)分它的設(shè)備是否真的產(chǎn)生了中斷。
內(nèi)核在接收一個中斷后,它將依次調(diào)用在該中斷線上注冊的共享的處理程序,所以,一個處理程序必須知道它是否應(yīng)該為這個中斷負(fù)責(zé),如果與它相關(guān)的設(shè)備并沒有產(chǎn)生中斷,那么處理程序應(yīng)該立即退出。
六、中斷上下文
當(dāng)執(zhí)行一個中斷處理程序的時候,內(nèi)核處于中斷上下文(interrupt context)中。
進(jìn)程上下文是一種內(nèi)核所處的操作模式,此時內(nèi)核代表進(jìn)程執(zhí)行。進(jìn)程上下文可以睡眠,也可以調(diào)用調(diào)度程序,因?yàn)檫M(jìn)程有 task_struct 結(jié)構(gòu),當(dāng)進(jìn)程再次被調(diào)度時能恢復(fù)進(jìn)程執(zhí)行環(huán)境。
中斷上下文和進(jìn)程沒有什么關(guān)聯(lián),并且中斷上下文是不可睡眠的,因?yàn)橹袛嗌舷挛臎]有某種結(jié)構(gòu)記錄它的執(zhí)行狀態(tài),一旦睡眠就無法再被重新喚起了(沒有東西來恢復(fù)它的執(zhí)行環(huán)境),所以在中斷上下文中不可使用信號量,因?yàn)樾盘柫繒?dǎo)致睡眠。因?yàn)橹袛啻驍嗔似渌a的執(zhí)行,所以中斷上下文的代碼應(yīng)該簡潔、迅速,盡量把工作從中斷處理程序中分離出來,放到下半部執(zhí)行。
procfs 是一個虛擬文件系統(tǒng),它只存在于內(nèi)核內(nèi)存,一般安裝于 /proc 目錄。在 procfs 中讀寫文件都要調(diào)用內(nèi)核函數(shù)。/proc/interrupt 文件存放系統(tǒng)中與中斷相關(guān)的統(tǒng)計(jì)信息。
通過禁止中斷,可以確保某個中斷處理程序不會搶占當(dāng)前的代碼。鎖提供保護(hù)機(jī)制,防止來自其他處理器的并發(fā)訪問,而禁止中斷提供保護(hù)機(jī)制,則是防止來自其他中斷處理程序的并發(fā)訪問。
禁止當(dāng)前處理器上的本地中斷,隨后又激活它們的語句為:
local_irq_disable();
/* 禁止中斷 */
local_irq_enable();
x86 上這兩個函數(shù)是通過單個匯編指令實(shí)現(xiàn)的,cli 指令和 sti 指令。
但是上述用法并不安全,萬一在調(diào)用?local_irq_disable()?之前中斷就是關(guān)閉的,之后再調(diào)用?local_irq_enable()相當(dāng)于無條件把中斷打開了,所以為了更安全的關(guān)閉中斷,我們使用如下方式:
unsigned long flags;
local_irq_save(flags); /* 禁止中斷 */
local_irq_restore(flags)l /* 中斷恢復(fù)到原來的狀態(tài) */
七、中斷下半部(bottom half)
下半部的任務(wù)就是執(zhí)行與中斷處理密切相關(guān)但中斷處理程序本身不執(zhí)行的工作。
- 對時間敏感的任務(wù),放到上半部。
- 和硬件相關(guān)的任務(wù),放到上半部。
- 如果一個任務(wù)要保證不被其他中斷打斷,放到上半部。
- 其他的任務(wù)考慮放到下半部。?
上半部執(zhí)行簡單快速,執(zhí)行時禁止中斷。下半部稍后執(zhí)行,執(zhí)行時能響應(yīng)所有中斷。這種設(shè)計(jì)可以使系統(tǒng)處于中斷屏蔽狀態(tài)的時間盡可能短,以此來提高系統(tǒng)的響應(yīng)能力。
實(shí)現(xiàn)下半部的方法有:軟中斷、?tasklet 和任務(wù)隊(duì)列、。
軟中斷
軟中斷是一組靜態(tài)定義的下半部接口,有32個,可以在所有處理器上同時執(zhí)行(這里提到的軟中斷和系統(tǒng)調(diào)用所用到的軟件中斷是不同的概念)。
軟中斷執(zhí)行函數(shù)如下:
asmlinkage void do_softirq(void)
2 {
3 __u32 pending;
4 unsigned long flags;
5
6 /* 判斷是否在中斷處理中,如果正在中斷處理,就直接返回 */
7 if (in_interrupt())
8 return;
9
10 /* 保存當(dāng)前寄存器的值 */
11 local_irq_save(flags);
12
13 /* 取得當(dāng)前已注冊軟中斷的位圖 */
14 pending = local_softirq_pending();
15
16 /* 循環(huán)處理所有已注冊的軟中斷 */
17 if (pending)
18 __do_softirq();
19
20 /* 恢復(fù)寄存器的值到中斷處理前 */
21 local_irq_restore(flags);
22 }
代碼的第一行判斷是否在中斷處理中,如果是則立刻退出函數(shù),這說明如果有軟中斷正在執(zhí)行,則其他軟中斷會返回。所以,軟中斷不能被另外一個軟中斷搶占!唯一可以搶占軟中斷的是中斷處理程序。雖然不能在本處理器上搶占,但其他的軟中斷可以在其他處理器上同時運(yùn)行,所以對于臨界區(qū)需要加鎖保護(hù)。
軟中斷留給對時間要求最嚴(yán)格的下半部使用。目前只有網(wǎng)絡(luò),內(nèi)核定時器和 tasklet 建立在軟中斷上。
Tasklet?
注意,這第二種機(jī)制是基于軟中斷實(shí)現(xiàn)的,靈活性強(qiáng),動態(tài)創(chuàng)建的下半部實(shí)現(xiàn)機(jī)制。兩個不同類型的 tasklet 可以在不同處理器上運(yùn)行,但相同的不可以,可以通過代碼動態(tài)注冊。
在 SMP 上,調(diào)用 tasklet 是會檢測 TASKLET_STATE_SCHED 標(biāo)志,如果同類型在運(yùn)行,就退出函數(shù)。
tasklet 由于是基于軟中斷實(shí)現(xiàn)的,所以也允許響應(yīng)中斷,但不能睡眠。
工作隊(duì)列
工作隊(duì)列(work queue)是另外一種將中斷的部分工作推后的一種方式,它可以實(shí)現(xiàn)一些tasklet不能實(shí)現(xiàn)的工作,比如工作隊(duì)列機(jī)制可以睡眠。這種差異的本質(zhì)原因是,在工作隊(duì)列機(jī)制中,將推后的工作交給一個稱之為工作者線程(worker thread)的內(nèi)核線程去完成(單核下一般會交給默認(rèn)的線程events/0)。因此,在該機(jī)制中,當(dāng)內(nèi)核在執(zhí)行中斷的剩余工作時就處在進(jìn)程上下文(process context)中。也就是說由工作隊(duì)列所執(zhí)行的中斷代碼會表現(xiàn)出進(jìn)程的一些特性,最典型的就是可以重新調(diào)度甚至睡眠。
對于tasklet機(jī)制(中斷處理程序也是如此),內(nèi)核在執(zhí)行時處于中斷上下文(interrupt context)中。而中斷上下文與進(jìn)程毫無瓜葛,所以在中斷上下文中就不能睡眠。因此,選擇tasklet還是工作隊(duì)列來完成下半部分應(yīng)該不難選擇。當(dāng)推后的那部分中斷程序需要睡眠時,工作隊(duì)列毫無疑問是你的最佳選擇;否則,還是用tasklet吧。文章來源:http://www.zghlxwxcb.cn/news/detail-662179.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-662179.html
到了這里,關(guān)于Linux內(nèi)核學(xué)習(xí)(六)—— 中斷(基于Linux 2.6內(nèi)核)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!