Linux內(nèi)核中斷系統(tǒng)處理機(jī)制(詳細(xì)分析~)
一、中斷概述
中斷是指在CPU正常運(yùn)行期間,由于內(nèi)外部事件或由程序預(yù)先安排的事件引起的CPU暫時(shí)停止正在運(yùn)行的程序,轉(zhuǎn)而為該內(nèi)部或外部事件或預(yù)先安排的事件服務(wù)的程序中去,服務(wù)完畢后再返回去繼續(xù)運(yùn)行被暫時(shí)中斷的程序。
1.1中斷類型
同步中斷由CPU本身產(chǎn)生,又稱為內(nèi)部中斷。這里同步是指中斷請(qǐng)求信號(hào)與代碼指令之間的同步執(zhí)行,在一條指令執(zhí)行完畢后,CPU才能進(jìn)行中斷,不能在執(zhí)行期間。所以也稱為異常(exception)。
異步中斷是由外部硬件設(shè)備產(chǎn)生,又稱為外部中斷,與同步中斷相反,異步中斷可在任何時(shí)間產(chǎn)生,包括指令執(zhí)行期間,所以也被稱為中斷(interrupt)。
異常又可分為可屏蔽中斷(Maskable interrupt)和非屏蔽中斷(Nomaskable interrupt)。而中斷可分為故障(fault)、陷阱(trap)、終止(abort)三類。
從廣義上講,中斷又可分為四類:中斷、故障、陷阱、終止。這些類別之間的異同點(diǎn)請(qǐng)參考 表 1。

有些參考資料中按照中斷來源進(jìn)行分類,如下圖所示(個(gè)人建議不采用這種方式):

1.2區(qū)分中斷號(hào)與中斷向量
I/O設(shè)備把中斷信號(hào)發(fā)送給中斷控制器(8259A)時(shí)與之相關(guān)聯(lián)的是一個(gè)中斷號(hào),當(dāng)中斷控制器把中斷信號(hào)發(fā)送給CPU時(shí)與之關(guān)聯(lián)的是一個(gè)中斷向量。換個(gè)角度分析就是中斷號(hào)是從中斷控制器層面劃分,中斷向量是從CPU層面劃分,所以中斷號(hào)與中斷向量之間存在一對(duì)一映射關(guān)系。在Intel X86中最大支持256種中斷,從0到255開始編號(hào),這個(gè)8位的編號(hào)就是中斷向量。其中將0到31保留用于異常處理和不可屏蔽中斷。
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ? ??


二、中斷數(shù)據(jù)處理結(jié)構(gòu)
Linux內(nèi)核中處理中斷主要有三個(gè)數(shù)據(jù)結(jié)構(gòu),irq_desc,irq_chip和irqaction。
在\include\linux\ irq.h中定義了
1)irq_desc用于描述IRQ線的屬性與狀態(tài),被稱為中斷描述符。
2)irq_chip用于描述不同類型的中斷控制器。
在\include\linux\ interrupt.h中定義了 irqaction用來描述特定設(shè)備所產(chǎn)生的中斷描述符。
三、Linux中斷機(jī)制
Linux中斷機(jī)制由三部分組成:
中斷子系統(tǒng)初始化:內(nèi)核自身初始化過程中對(duì)中斷處理機(jī)制初始化,例如中斷的數(shù)據(jù)結(jié)構(gòu)以及中斷請(qǐng)求等。
中斷或異常處理:中斷整體處理過程。
中斷API:為設(shè)備驅(qū)動(dòng)提供API,例如注冊(cè),釋放和激活等。
3.1中斷子系統(tǒng)初始化
3.1.1中斷描述符表(IDT)初始化
中斷描述符表初始化需要經(jīng)過兩個(gè)過程:
第一個(gè)過程在內(nèi)核引導(dǎo)過程。由兩個(gè)步驟組成,首先給分配IDT分配2KB空間(256中斷向量,每個(gè)向量由8bit組成)并初始化;然后把IDT起始地址存儲(chǔ)到IDTR寄存器中。
第二個(gè)過程內(nèi)核在初始化自身的start_kernal函數(shù)中使用trap_init初始化系統(tǒng)保留中斷向量,使用init_IRQ完成其余中斷向量初始化。
3.1.2中斷請(qǐng)求隊(duì)列初始化
init_IRQ調(diào)用pre_intr_init_hook,進(jìn)而最終調(diào)用init_ISA_irqs初始化中斷控制器以及每個(gè)IRQ線的中斷請(qǐng)求隊(duì)列。
3.2中斷或異常處理
中斷處理過程:設(shè)備產(chǎn)生中斷,并通過中斷線將中斷信號(hào)送往中斷控制器,如果中斷沒有被屏蔽則會(huì)到達(dá)CPU的INTR引腳,CPU立即停止當(dāng)前工作,根據(jù)獲得中斷向量號(hào)從IDT中找出門描述符,并執(zhí)行相關(guān)中斷程序。
異常處理過程:異常是由CPU內(nèi)部發(fā)生所以不會(huì)通過中斷控制器,CPU直接根據(jù)中斷向量號(hào)從IDT中找出門描述符,并執(zhí)行相關(guān)中斷程序。

中斷控制器處理主要有5個(gè)步驟:1.中斷請(qǐng)求 2.中斷響應(yīng) 3.優(yōu)先級(jí)比較 4.提交中斷向量 5.中斷結(jié)束。這里不再贅述5個(gè)步驟的具體流程。
CPU處理流程主要有6個(gè)步驟:1.確定中斷或異常的中斷向量 2.通過IDTR寄存器找到IDT 3.特權(quán)檢查 4.特權(quán)級(jí)發(fā)生變化,進(jìn)行堆棧切換 5.如果是異常將異常代碼壓入堆棧,如果是中斷則關(guān)閉可屏蔽中斷 6.進(jìn)入中斷或異常服務(wù)程序執(zhí)行。這里不再贅述6個(gè)步驟的具體流程。
3.3中斷API
內(nèi)核提供的API主要用于驅(qū)動(dòng)的開發(fā)。
注冊(cè)IRQ:
釋放IRQ:
注:IRQ線資源非常寶貴,我們?cè)谑褂脮r(shí)必須先注冊(cè),不使用時(shí)必須釋放IRQ資源。
激活當(dāng)前CPU中斷:
禁止當(dāng)前CPU中斷:
激活指定中斷線:
禁止指定中斷線:
禁止指定中斷線:
注:此函數(shù)調(diào)用irq_chip中disable禁止指定中斷線,所以不會(huì)保證中斷線上執(zhí)行的中斷服務(wù)程序已經(jīng)退出。
3.4中斷機(jī)制劃分
由于中斷會(huì)打斷內(nèi)核中進(jìn)程的正常調(diào)度運(yùn)行,所以要求中斷服務(wù)程序盡可能的短小精悍;但是在實(shí)際系統(tǒng)中,當(dāng)中斷到來時(shí),要完成工作往往進(jìn)行大量的耗時(shí)處理。因此期望讓中斷處理程序運(yùn)行得快,并想讓它完成的工作量多,這兩個(gè)目標(biāo)相互制約,誕生——頂/底半部機(jī)制。
中斷處理程序是頂半部——接受中斷,它就立即開始執(zhí)行,但只有做嚴(yán)格時(shí)限的工作。能夠被允許稍后完成的工作會(huì)推遲到底半部去,此后,在合適的時(shí)機(jī),底半部會(huì)被開終端執(zhí)行。頂半部簡(jiǎn)單快速,執(zhí)行時(shí)禁止一些或者全部中斷。
底半部稍后執(zhí)行,而且執(zhí)行期間可以響應(yīng)所有的中斷。這種設(shè)計(jì)可以使系統(tǒng)處于中斷屏蔽狀態(tài)的時(shí)間盡可能的短,以此來提高系統(tǒng)的響應(yīng)能力。頂半部只有中斷處理程序機(jī)制,而底半部的實(shí)現(xiàn)有軟中斷,tasklet和工作隊(duì)列實(shí)現(xiàn)。

3.4.1頂/底半部劃分原則:
如果一個(gè)任務(wù)對(duì)時(shí)間非常敏感,將其放在頂半部中執(zhí)行;
如果一個(gè)任務(wù)和硬件有關(guān),將其放在頂半部中執(zhí)行;
如果一個(gè)任務(wù)要保證不被其他中斷打斷,將其放在頂半部中執(zhí)行;
其他所有任務(wù),考慮放置在底半部執(zhí)行。
3.4.2底半部實(shí)現(xiàn)機(jī)制

軟中斷:
軟中斷作為下半部機(jī)制的代表,是隨著SMP(share memory processor)的出現(xiàn)應(yīng)運(yùn)而生的,它也是tasklet實(shí)現(xiàn)的基礎(chǔ)(tasklet實(shí)際上只是在軟中斷的基礎(chǔ)上添加了一定的機(jī)制)。軟中斷一般是“可延遲函數(shù)”的總稱,有時(shí)候也包括了tasklet(請(qǐng)讀者在遇到的時(shí)候根據(jù)上下文推斷是否包含tasklet)。它的出現(xiàn)就是因?yàn)橐獫M足上面所提出的上半部和下半部的區(qū)別,使得對(duì)時(shí)間不敏感的任務(wù)延后執(zhí)行,軟中斷執(zhí)行中斷處理程序留給它去完成的剩余任務(wù),而且可以在多個(gè)CPU上并行執(zhí)行,使得總的系統(tǒng)效率可以更高。它的特性包括:
產(chǎn)生后并不是馬上可以執(zhí)行,必須要等待內(nèi)核的調(diào)度才能執(zhí)行。軟中斷不能被自己打斷,只能被硬件中斷打斷(上半部)。
可以并發(fā)運(yùn)行在多個(gè)CPU上(即使同一類型的也可以)。所以軟中斷必須設(shè)計(jì)為可重入的函數(shù)(允許多個(gè)CPU同時(shí)操作),因此也需要使用自旋鎖來保護(hù)其數(shù)據(jù)結(jié)構(gòu)。
內(nèi)核中定義了幾種軟中斷的用途:
Tasklet:
tasklet是通過軟中斷實(shí)現(xiàn)的,所以它本身也是軟中斷。
軟中斷用輪詢的方式處理。假如正好是最后一種中斷,則必須循環(huán)完所有的中斷類型,才能最終執(zhí)行對(duì)應(yīng)的處理函數(shù)。顯然當(dāng)年開發(fā)人員為了保證輪詢的效率,于是限制中斷個(gè)數(shù)為32個(gè)。
為了提高中斷處理數(shù)量,順道改進(jìn)處理效率,于是產(chǎn)生了tasklet機(jī)制。
Tasklet采用無差別的隊(duì)列機(jī)制,有中斷時(shí)才執(zhí)行,免去了循環(huán)查表之苦。Tasklet作為一種新機(jī)制,顯然可以承擔(dān)更多的優(yōu)點(diǎn)。正好這時(shí)候SMP越來越火了,因此又在tasklet中加入了SMP機(jī)制,保證同種中斷只能在一個(gè)cpu上執(zhí)行。在軟中斷時(shí)代,顯然沒有這種考慮。因此同一種軟中斷可以在兩個(gè)cpu上同時(shí)執(zhí)行,很可能造成沖突。
總結(jié)下tasklet的優(yōu)點(diǎn):
無類型數(shù)量限制;
效率高,無需循環(huán)查表;
支持SMP機(jī)制;
它的特性如下:
一種特定類型的tasklet只能運(yùn)行在一個(gè)CPU上,不能并行,只能串行執(zhí)行。
多個(gè)不同類型的tasklet可以并行在多個(gè)CPU上。
軟中斷是靜態(tài)分配的,在內(nèi)核編譯好之后,就不能改變。但tasklet就靈活許多,可以在運(yùn)行時(shí)改變(比如添加模塊時(shí))。
工作隊(duì)列:
上面我們介紹的可延遲函數(shù)運(yùn)行在中斷上下文中,于是導(dǎo)致了一些問題,說明它們不可掛起,也就是說軟中斷不能睡眠、不能阻塞,原因是由于中斷上下文出于內(nèi)核態(tài),沒有進(jìn)程切換,所以如果軟中斷一旦睡眠或者阻塞,將無法退出這種狀態(tài),導(dǎo)致內(nèi)核會(huì)整個(gè)僵死。因此,可阻塞函數(shù)不能用軟中斷來實(shí)現(xiàn)。但是它們往往又具有可延遲的特性。而且由于是串行執(zhí)行,因此只要有一個(gè)處理時(shí)間較長(zhǎng),則會(huì)導(dǎo)致其他中斷響應(yīng)的延遲。為了完成這些不可能完成的任務(wù),于是出現(xiàn)了工作隊(duì)列,它能夠在不同的進(jìn)程間切換,以完成不同的工作。
工作隊(duì)列能運(yùn)行在進(jìn)程上下文,它將工作給一個(gè)內(nèi)核線程,作為中斷守護(hù)線程來使用。多個(gè)中斷可以放在一個(gè)線程中,也可以每個(gè)中斷分配一個(gè)線程。我們用結(jié)構(gòu)體workqueue_struct表示工作者線程,工作者線程是用內(nèi)核線程實(shí)現(xiàn)的。而工作者線程是如何執(zhí)行被推后的工作——有這樣一個(gè)鏈表,它由結(jié)構(gòu)體work_struct組成,而這個(gè)work_struct則描述了一個(gè)工作,一旦這個(gè)工作被執(zhí)行完,相應(yīng)的work_struct對(duì)象就從鏈表上移去,當(dāng)鏈表上不再有對(duì)象時(shí),工作者線程就會(huì)繼續(xù)休眠。因?yàn)楣ぷ麝?duì)列是線程,所以我們可以使用所有可以在線程中使用的方法。
如何選擇下半部機(jī)制:
軟中斷和tasklet運(yùn)行在中斷上下文,工作隊(duì)列運(yùn)行在進(jìn)程上下文。如果需要休眠則選擇工作隊(duì)列,否則選擇tasklet;如果對(duì)性能要求較高則選擇軟中斷。
從易用性考慮,首選工作隊(duì)列,然后tasklet,最后是軟中斷,因?yàn)檐浿袛嘈枰o態(tài)創(chuàng)建。
從代碼安全考慮,如果對(duì)底半部代碼保護(hù)不夠安全,則選擇tasklet,因?yàn)橄鄬?duì)軟中斷,tasklet對(duì)鎖要求低,上面也簡(jiǎn)述它們工作方式以及運(yùn)用場(chǎng)景。
四、多處理器系統(tǒng)中斷相關(guān)概念
4.1處理器間中斷
在多處理器系統(tǒng)中,操作系統(tǒng)需要在多個(gè)處理器中協(xié)調(diào)操作,所以需要處理器中斷(Inter-Processor-Interrupt,IPI)實(shí)現(xiàn),IPI是一種特殊硬件中斷,由CPU送出,其他CPU接收,處理CPU之間通信和同步操作。以下是x86中SMP定義的IPI,中斷向量號(hào)用十六進(jìn)制表示:
4.2中斷親和力
將一個(gè)或多個(gè)中斷服務(wù)程序綁定到特定的CPU上處理,這就是中斷親和力(SMP IRQ affinity)。我們可以使用中斷親和力來均衡各個(gè)CPU的負(fù)載,提高系統(tǒng)處理能力。
