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

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

一文淺析Linux 中斷處理

2023-10-19 15:50 作者:補給站Linux內(nèi)核  | 我要投稿

1. 中斷的概念

中斷是指在CPU正常運行期間,由于內(nèi)外部事件或由程序預(yù)先安排的事件引起的 CPU 暫時停止正在運行的程序,轉(zhuǎn)而為該內(nèi)部或外部事件或預(yù)先安排的事件服務(wù)的程序中去,服務(wù)完畢后再返回去繼續(xù)運行被暫時中斷的程序。Linux中通常分為外部中斷(又叫硬件中斷)和內(nèi)部中斷(又叫異常)。

軟件對硬件進(jìn)行配置后,軟件期望等待硬件的某種狀態(tài)(比如,收到了數(shù)據(jù)),這里有兩種方式,一種是輪詢(polling):CPU 不斷的去讀硬件狀態(tài)。另一種是當(dāng)硬件完成某種事件后,給 CPU 一個中斷,讓 CPU 停下手上的事情,去處理這個中斷。很顯然,中斷的交互方式提高了系統(tǒng)的吞吐。

當(dāng) CPU 收到一個中斷 (IRQ)的時候,會去執(zhí)行該中斷對應(yīng)的處理函數(shù)(ISR)。普通情況下,會有一個中斷向量表,向量表中定義了 CPU 對應(yīng)的每一個外設(shè)資源的中斷處理程序的入口,當(dāng)發(fā)生對應(yīng)的中斷的時候, CPU 直接跳轉(zhuǎn)到這個入口執(zhí)行程序。也就是中斷上下文。(注意:中斷上下文中,不可阻塞睡眠)。

2. Linux 中斷 top/bottom

玩過 MCU 的人都知道,中斷服務(wù)程序的設(shè)計最好是快速完成任務(wù)并退出,因為此刻系統(tǒng)處于被中斷中。但是在 ISR 中又有一些必須完成的事情,比如:清中斷標(biāo)志,讀/寫數(shù)據(jù),寄存器操作等。

在 Linux 中,同樣也是這個要求,希望盡快的完成 ISR。但事與愿違,有些 ISR 中任務(wù)繁重,會消耗很多時間,導(dǎo)致響應(yīng)速度變差。Linux 中針對這種情況,將中斷分為了兩部分:

1. 上半部(top half):收到一個中斷,立即執(zhí)行,有嚴(yán)格的時間限制,只做一些必要的工作,比如:應(yīng)答,復(fù)位等。這些工作都是在所有中斷被禁止的情況下完成的。

2. 底半部(bottom half):能夠被推遲到后面完成的任務(wù)會在底半部進(jìn)行。在適合的時機(jī),下半部會被開中斷執(zhí)行。

3. 中斷處理程序

驅(qū)動程序可以使用接口:

像系統(tǒng)申請注冊一個中斷處理程序。

其中的參數(shù):


中斷標(biāo)志 flag 的含義:


調(diào)用 request _irq 成功執(zhí)行返回 0。常見錯誤是 -EBUSY,表示給定的中斷線已經(jīng)在使用(或者沒有指定 IRQF_SHARED)

注意:request_irq函數(shù)可能引起睡眠,所以不允許在中斷上下文或者不允許睡眠的代碼中調(diào)用。

釋放中斷:

用于釋放中斷處理函數(shù)。

注意:Linux 中的中斷處理程序是無須重入的。當(dāng)給定的中斷處理程序正在執(zhí)行的時候,其中斷線在所有的處理器上都會被屏蔽掉,以防在同一個中斷線上又接收到另一個新的中斷。通常情況下,除了該中斷的其他中斷都是打開的,也就是說其他的中斷線上的重點都能夠被處理,但是當(dāng)前的中斷線總是被禁止的,故,同一個中斷處理程序是絕對不會被自己嵌套的。

4. 中斷上下文

與進(jìn)程上下文不一樣,內(nèi)核執(zhí)行中斷服務(wù)程序的時候,處于中斷上下文。中斷處理程序并沒有自己的獨立的棧,而是使用了內(nèi)核棧,其大小一般是有限制的(32bit 機(jī)器 8KB)。所以其必須短小精悍。同時中斷服務(wù)程序是打斷了正常的程序流程,這一點上也必須保證快速的執(zhí)行。同時中斷上下文中是不允許睡眠,阻塞的。

中斷上下文不能睡眠的原因是:

1、 中斷處理的時候,不應(yīng)該發(fā)生進(jìn)程切換,因為在中斷context中,唯一能打斷當(dāng)前中斷handler的只有更高優(yōu)先級的中斷,它不會被進(jìn)程打斷,如果在 中斷context中休眠,則沒有辦法喚醒它,因為所有的wake_up_xxx都是針對某個進(jìn)程而言的,而在中斷context中,沒有進(jìn)程的概念,沒 有一個task_struct(這點對于softirq和tasklet一樣),因此真的休眠了,比如調(diào)用了會導(dǎo)致block的例程,內(nèi)核幾乎肯定會死。

2、schedule()在切換進(jìn)程時,保存當(dāng)前的進(jìn)程上下文(CPU寄存器的值、進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容),以便以后恢復(fù)此進(jìn)程運行。中斷發(fā)生后,內(nèi)核會先保存當(dāng)前被中斷的進(jìn)程上下文(在調(diào)用中斷處理程序后恢復(fù));

但在中斷處理程序里,CPU寄存器的值肯定已經(jīng)變化了吧(最重要的程序計數(shù)器PC、堆棧SP等),如果此時因為睡眠或阻塞操作調(diào)用了schedule(),則保存的進(jìn)程上下文就不是當(dāng)前的進(jìn)程context了.所以不可以在中斷處理程序中調(diào)用schedule()。

3、內(nèi)核中schedule()函數(shù)本身在進(jìn)來的時候判斷是否處于中斷上下文:

if(unlikely(in_interrupt()))

BUG();

因此,強(qiáng)行調(diào)用schedule()的結(jié)果就是內(nèi)核BUG。

4、中斷handler會使用被中斷的進(jìn)程內(nèi)核堆棧,但不會對它有任何影響,因為handler使用完后會完全清除它使用的那部分堆棧,恢復(fù)被中斷前的原貌。

5、處于中斷context時候,內(nèi)核是不可搶占的。因此,如果休眠,則內(nèi)核一定掛起。

5. 舉例

比如 RTC 驅(qū)動程序 (drivers/char/rtc.c)。在 RTC 驅(qū)動的初始化階段,會調(diào)用到 rtc_init 函數(shù):

在這個初始化函數(shù)中調(diào)用到了 request_irq 用于申請中斷資源,并注冊服務(wù)程序:

RTC_IRQ 是中斷號,和處理器綁定。

rtc_interrupt 是中斷處理程序:

每次收到 RTC 中斷,就會調(diào)用進(jìn)這個函數(shù)。


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

零聲白金VIP體驗卡(含基礎(chǔ)架構(gòu)/高性能存儲/golang/QT/音視頻/Linux內(nèi)核)課程:??

6. 中斷處理流程

發(fā)生中斷時,CPU執(zhí)行異常向量vector_irq的代碼, 即異常向量表中的中斷異常的代碼,它是一個跳轉(zhuǎn)指令,跳去執(zhí)行真正的中斷處理程序,在vector_irq里面,最終會調(diào)用中斷處理的總?cè)肟诤瘮?shù)。

C 語言的入口為 :asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

該函數(shù)的入?yún)?irq 為中斷號。

asm_do_IRQ -> handle_IRQ

handle_IRQ -> __handle_domain_irq

這里請注意:

先調(diào)用了 irq_enter 標(biāo)記進(jìn)入了硬件中斷:

irq_enter是更新一些系統(tǒng)的統(tǒng)計信息,同時在__irq_enter宏中禁止了進(jìn)程的搶占。雖然在產(chǎn)生IRQ時,ARM會自動把CPSR中的I位置位,禁止新的IRQ請求,直到中斷控制轉(zhuǎn)到相應(yīng)的流控層后才通過local_irq_enable()打開。那為何還要禁止搶占?這是因為要考慮中斷嵌套的問題,一旦流控層或驅(qū)動程序主動通過local_irq_enable打開了IRQ,而此時該中斷還沒處理完成,新的irq請求到達(dá),這時代碼會再次進(jìn)入irq_enter,在本次嵌套中斷返回時,內(nèi)核不希望進(jìn)行搶占調(diào)度,而是要等到最外層的中斷處理完成后才做出調(diào)度動作,所以才有了禁止搶占這一處理

再調(diào)用 generic_handle_irq

最后調(diào)用 irq_exit 刪除進(jìn)入硬件中斷的標(biāo)記

__handle_domain_irq -> generic_handle_irq

首先在函數(shù)irq_to_desc中根據(jù)發(fā)生中斷的中斷號,去取出它的 irq_desc 中斷描述結(jié)構(gòu),然后調(diào)用 generic_handle_irq_desc:

這里調(diào)用了 handle_irq 函數(shù)。

所以,在上述流程中,還需要分析 irq_to_desc ?流程:

NR_IRQS 是支持的總的中斷個數(shù),當(dāng)然,irq 不能夠大于這個數(shù)目。所以返回 irq_desc + irq。

irq_desc 是一個全局的數(shù)組:

這里是這個數(shù)組的初始化的地方。所有的 handle_irq 函數(shù)都被初始化成為了 handle_bad_irq。

細(xì)心的觀眾可能發(fā)現(xiàn)了,調(diào)用這個 desc->handle_irq(desc) 函數(shù),并不是咱們注冊進(jìn)去的中斷處理函數(shù)啊,因為兩個函數(shù)的原型定義都不一樣。這個 handle_irq 是 irq_flow_handler_t 類型,而我們注冊進(jìn)去的服務(wù)程序是 irq_handler_t,這兩個明顯不是同一個東西,所以這里我們還需要繼續(xù)分析。

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

Linux 中斷相關(guān)的數(shù)據(jù)結(jié)構(gòu)有 3 個


irq_desc 結(jié)構(gòu)如下


irqaction 結(jié)構(gòu)如下:



irq_chip 描述如下:


irq_chip 是一串和芯片相關(guān)的函數(shù)指針,這里定義的非常的全面,基本上和 IRQ 相關(guān)的可能出現(xiàn)的操作都全部定義進(jìn)去了,具體根據(jù)不同的芯片,需要在不同的芯片的地方去初始化這個結(jié)構(gòu),然后這個結(jié)構(gòu)會嵌入到通用的 IRQ 處理軟件中去使用,使得軟件處理邏輯和芯片邏輯完全的分開。

好,我們接下來繼續(xù)前進(jìn)。

6.2 初始化 Chip 相關(guān)的 IRQ

眾所周知,啟動的時候,C 語言從 start_kernel 開始,在這里面,調(diào)用了和 machine 相關(guān)的 IRQ 的初始化 init_IRQ():

在 init_IRQ 中,調(diào)用了machine_desc->init_irq():



machine_desc->init_irq()?完成對中斷控制器的初始化,為每個irq_desc結(jié)構(gòu)安裝合適的流控handler,為每個irq_desc結(jié)構(gòu)安裝irq_chip指針,使他指向正確的中斷控制器所對應(yīng)的irq_chip結(jié)構(gòu)的實例,同時,如果該平臺中的中斷線有多路復(fù)用(多個中斷公用一個irq中斷線)的情況,還應(yīng)該初始化irq_desc中相應(yīng)的字段和標(biāo)志,以便實現(xiàn)中斷控制器的級聯(lián)。

這里初始化的時候回調(diào)用到具體的芯片相關(guān)的中斷初始化的地方。

例如:

而在這些里面,都回去調(diào)用類似于:


這些函數(shù)定義在 include/linux/irq.h 文件。是對芯片初始化的時候可見的 APIs,用于指定中斷“流控”中的 :

irq_flow_handler_t handle

也就是中斷來的時候,最后那個函數(shù)調(diào)用。

中斷流控函數(shù),分幾種,電平觸發(fā)的中斷,邊沿觸發(fā)的,等:

而在這些處理函數(shù)里,會去調(diào)用到 :handle_irq_event

比如:


而這個 handle_irq_event 則是調(diào)用了處理,handle_irq_event_percpu:




handle_irq_event_percpu->__handle_irq_event_percpu-> 【action->handler()】

這里終于看到了調(diào)用 的地方了,就是咱們通過 request_irq 注冊進(jìn)去的函數(shù)

7. /proc/interrupts

這個 proc 下放置了對應(yīng)中斷號的中斷次數(shù)和對應(yīng)的 dev-name。


原文作者:【一起學(xué)嵌入式



一文淺析Linux 中斷處理的評論 (共 條)

分享到微博請遵守國家法律
孟州市| 樟树市| 屏边| 巴青县| 石林| 五寨县| 海城市| 平舆县| 天祝| 治县。| 波密县| 民丰县| 牙克石市| 北辰区| 琼海市| 扶风县| 鄂托克前旗| 凌海市| 类乌齐县| 故城县| 泰州市| 临西县| 宁乡县| 望城县| 花垣县| 若羌县| 台山市| 托里县| 宜章县| 郴州市| 新蔡县| 永靖县| 河源市| 株洲市| 股票| 郁南县| 察雅县| 新野县| 红安县| 东光县| 郴州市|