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

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

一文完全讀懂--Linux內(nèi)核中斷處理

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

一、什么是中斷

  • 中斷 是為了解決外部設(shè)備完成某些工作后通知CPU的一種機(jī)制(譬如硬盤完成讀寫操作后通過中斷告知CPU已經(jīng)完成)。早期沒有中斷機(jī)制的計算機(jī)就不得不通過輪詢來查詢外部設(shè)備的狀態(tài),由于輪詢是試探查詢的(也就是說設(shè)備不一定是就緒狀態(tài)),所以往往要做很多無用的查詢,從而導(dǎo)致效率非常低下。由于中斷是由外部設(shè)備主動通知CPU的,所以不需要CPU進(jìn)行輪詢?nèi)ゲ樵?,效率大大提升?/p>

  • 從物理學(xué)的角度看,中斷是一種電信號,由硬件設(shè)備產(chǎn)生,并直接送入中斷控制器(如 8259A)的輸入引腳上,然后再由中斷控制器向處理器發(fā)送相應(yīng)的信號。處理器一經(jīng)檢測到該信號,便中斷自己當(dāng)前正在處理的工作,轉(zhuǎn)而去處理中斷。此后,處理器會通知 OS 已經(jīng)產(chǎn)生中斷。這樣,OS 就可以對這個中斷進(jìn)行適當(dāng)?shù)奶幚?。不同的設(shè)備對應(yīng)的中斷不同,而每個中斷都通過一個唯一的數(shù)字標(biāo)識,這些值通常被稱為中斷請求線。

二、中斷控制器

  • X86計算機(jī)的 CPU 為中斷只提供了兩條外接引腳:NMI 和 INTR。其中 NMI 是不可屏蔽中斷,它通常用于電源掉電和物理存儲器奇偶校驗(yàn);INTR是可屏蔽中斷,可以通過設(shè)置中斷屏蔽位來進(jìn)行中斷屏蔽,它主要用于接受外部硬件的中斷信號,這些信號由中斷控制器傳遞給 CPU。

  • 常見的中斷控制器有兩種:

可編程中斷控制器8259A

  • 傳統(tǒng)的 PIC(Programmable Interrupt Controller,可編程中斷控制器)是由兩片 8259A 風(fēng)格的外部芯片以“級聯(lián)”的方式連接在一起。每個芯片可處理多達(dá) 8 個不同的 IRQ。因?yàn)閺?PIC 的 INT 輸出線連接到主 PIC 的 IRQ2 引腳,所以可用 IRQ 線的個數(shù)達(dá)到 15 個,如圖下所示。


  • 8259A 高級可編程中斷控制器(APIC)

  • 8259A 只適合單 CPU 的情況,為了充分挖掘 SMP 體系結(jié)構(gòu)的并行性,能夠把中斷傳遞給系統(tǒng)中的每個 CPU 至關(guān)重要。基于此理由,Intel 引入了一種名為 I/O 高級可編程控制器的新組件,來替代老式的 8259A 可編程中斷控制器。該組件包含兩大組成部分:一是“本地 APIC”,主要負(fù)責(zé)傳遞中斷信號到指定的處理器;舉例來說,一臺具有三個處理器的機(jī)器,則它必須相對的要有三個本地 APIC。另外一個重要的部分是 I/O APIC,主要是收集來自 I/O 裝置的 Interrupt 信號且在當(dāng)那些裝置需要中斷時發(fā)送信號到本地 APIC,系統(tǒng)中最多可擁有 8 個 I/O APIC。

  • 每個本地 APIC 都有 32 位的寄存器,一個內(nèi)部時鐘,一個本地定時設(shè)備以及為本地中斷保留的兩條額外的 IRQ 線 LINT0 和 LINT1。所有本地 APIC 都連接到 I/O APIC,形成一個多級 APIC 系統(tǒng),如圖下所示。

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。?!前100名進(jìn)群領(lǐng)取,額外贈送一份價值699的內(nèi)核資料包(含視頻教程、電子書、實(shí)戰(zhàn)項目及代碼)??

?

APIC

  • 目前大部分單處理器系統(tǒng)都包含一個 I/O APIC 芯片,可以通過以下兩種方式來對這種芯片進(jìn)行配置:

  1. 作為一種標(biāo)準(zhǔn)的 8259A 工作方式。本地 APIC 被禁止,外部 I/O APIC 連接到 CPU,兩條 LINT0 和 LINT1 分別連接到 INTR 和 NMI 引腳。

  2. 作為一種標(biāo)準(zhǔn)外部 I/O APIC。本地 APIC 被激活,且所有的外部中斷都通過 I/O APIC 接收。

  • 辨別一個系統(tǒng)是否正在使用 I/O APIC,可以在命令行輸入如下命令:

  • 如果輸出結(jié)果中列出了 IO-APIC,說明您的系統(tǒng)正在使用 APIC。如果看到 XT-PIC,意味著您的系統(tǒng)正在使用 8259A 芯片。

中斷分類

  • 中斷可分為同步(synchronous)中斷和異步(asynchronous)中斷:

  1. 同步中斷是當(dāng)指令執(zhí)行時由 CPU 控制單元產(chǎn)生,之所以稱為同步,是因?yàn)橹挥性谝粭l指令執(zhí)行完畢后 CPU 才會發(fā)出中斷,而不是發(fā)生在代碼指令執(zhí)行期間,比如系統(tǒng)調(diào)用。

  2. 異步中斷是指由其他硬件設(shè)備依照 CPU 時鐘信號隨機(jī)產(chǎn)生,即意味著中斷能夠在指令之間發(fā)生,例如鍵盤中斷。

  • 根據(jù) Intel 官方資料,同步中斷稱為異常(exception),異步中斷被稱為中斷(interrupt)。

  • 中斷可分為 可屏蔽中斷 (Maskable interrupt)和 非屏蔽中斷 (Nomaskable interrupt)。異??煞譃?故障 (fault)、 陷阱 (trap)、 終止 (abort)三類。

  • 從廣義上講,中斷可分為四類: 中斷 、 故障 、 陷阱 、 終止 。這些類別之間的異同點(diǎn)請參看 表。 表:中斷類別及其行為

  • X86 體系結(jié)構(gòu)的每個中斷都被賦予一個唯一的編號或者向量(8 位無符號整數(shù))。非屏蔽中斷和異常向量是固定的,而可屏蔽中斷向量可以通過對中斷控制器的編程來改變。

中斷處理 - 上半部(硬中斷)

  • 由于 APIC中斷控制器 有點(diǎn)小復(fù)雜,所以本文主要通過 8259A中斷控制器 來介紹Linux對中斷的處理過程。 中斷處理相關(guān)結(jié)構(gòu)

  • 前面說過, 8259A中斷控制器 由兩片 8259A 風(fēng)格的外部芯片以 級聯(lián) 的方式連接在一起,每個芯片可處理多達(dá) 8 個不同的 IRQ(中斷請求),所以可用 IRQ 線的個數(shù)達(dá)到 15 個。如下圖:

8259A

  • 在內(nèi)核中每條IRQ線由結(jié)構(gòu)體 irq_desc_t 來描述, irq_desc_t 定義如下:

  • 下面介紹一下 irq_desc_t 結(jié)構(gòu)各個字段的作用:

  1. status : IRQ線的狀態(tài)。

  2. handler : 類型為 hw_interrupt_type 結(jié)構(gòu),表示IRQ線對應(yīng)的硬件相關(guān)處理函數(shù),比如 8259A中斷控制器 接收到一個中斷信號時,需要發(fā)送一個確認(rèn)信號才會繼續(xù)接收中斷信號的,發(fā)送確認(rèn)信號的函數(shù)就是 hw_interrupt_type 中的 ack 函數(shù)。

  3. action : 類型為 irqaction 結(jié)構(gòu),中斷信號的處理入口。由于一條IRQ線可以被多個硬件共享,所以 action 是一個鏈表,每個 action 代表一個硬件的中斷處理入口。

  4. depth : 防止多次開啟和關(guān)閉IRQ線。

  5. lock : 防止多核CPU同時對IRQ進(jìn)行操作的自旋鎖。

  • hw_interrupt_type 這個結(jié)構(gòu)與硬件相關(guān),這里就不作介紹了,我們來看看 irqaction 這個結(jié)構(gòu):

  • 下面說說 irqaction 結(jié)構(gòu)各個字段的作用:

  1. handler : 中斷處理的入口函數(shù), handler 的第一個參數(shù)是中斷號,第二個參數(shù)是設(shè)備對應(yīng)的ID,第三個參數(shù)是中斷發(fā)生時由內(nèi)核保存的各個寄存器的值。

  2. flags : 標(biāo)志位,用于表示 irqaction 的一些行為,例如是否能夠與其他硬件共享IRQ線。

  3. name : 用于保存中斷處理的名字。

  4. dev_id : 設(shè)備ID。

  5. next : 每個硬件的中斷處理入口對應(yīng)一個 irqaction 結(jié)構(gòu),由于多個硬件可以共享同一條IRQ線,所以這里通過 next 字段來連接不同的硬件中斷處理入口。

  • irq_desc_t 結(jié)構(gòu)關(guān)系如下圖:


  • irq_desc_t 注冊中斷處理入口

  • 在內(nèi)核中,可以通過 setup_irq 函數(shù)來注冊一個中斷處理入口。 setup_irq 函數(shù)代碼如下:

setup_irq 函數(shù)比較簡單,就是通過 irq 號來查找對應(yīng)的 irq_desc_t 結(jié)構(gòu),并把新的 irqaction 連接到 irq_desc_t 結(jié)構(gòu)的 action 鏈表中。要注意的是,如果設(shè)備不支持共享IRQ線(也即是 flags 字段沒有設(shè)置 SA_SHIRQ 標(biāo)志),那么就返回 EBUSY 錯誤。 我們看看 時鐘中斷處理入口 的注冊實(shí)例:


  • 可以看到,時鐘中斷處理入口的IRQ號為0,處理函數(shù)為 timer_interrupt ,并且不支持共享IRQ線( flags 字段沒有設(shè)置 SA_SHIRQ 標(biāo)志)。

處理中斷請求

  • 當(dāng)一個中斷發(fā)生時,中斷控制層會發(fā)送信號給CPU,CPU收到信號會中斷當(dāng)前的執(zhí)行,轉(zhuǎn)而執(zhí)行中斷處理過程。中斷處理過程首先會保存寄存器的值到棧中,然后調(diào)用 do_IRQ 函數(shù)進(jìn)行進(jìn)一步的處理, do_IRQ 函數(shù)代碼如下:

  • do_IRQ 函數(shù)首先通過IRQ號獲取到其對應(yīng)的 irq_desc_t 結(jié)構(gòu),注意的是同一個中斷有可能發(fā)生多次,所以要判斷當(dāng)前IRQ是否正在被處理當(dāng)中(判斷 irq_desc_t 結(jié)構(gòu)的 status 字段是否設(shè)置了 IRQ_INPROGRESS 標(biāo)志),如果不是處理當(dāng)前,那么就獲取到 action 鏈表,然后通過調(diào)用 handle_IRQ_event 函數(shù)來執(zhí)行 action 鏈表中的中斷處理函數(shù)。

  • 如果在處理中斷的過程中又發(fā)生了相同的中斷( irq_desc_t 結(jié)構(gòu)的 status 字段被設(shè)置了 IRQ_INPROGRESS 標(biāo)志),那么就繼續(xù)對中斷進(jìn)行處理。處理完中斷后,調(diào)用 do_softirq 函數(shù)來對中斷下半部進(jìn)行處理(下面會說)。

  • 接下來看看 handle_IRQ_event 函數(shù)的實(shí)現(xiàn):

  • andle_IRQ_event 函數(shù)非常簡單,就是遍歷 action 鏈表并且執(zhí)行其中的處理函數(shù),比如對于 時鐘中斷 就是調(diào)用 timer_interrupt 函數(shù)。這里要注意的是,如果中斷處理過程能夠開啟中斷的,那么就把中斷打開(因?yàn)镃PU接收到中斷信號時會關(guān)閉中斷)。

中斷處理 - 下半部(軟中斷)

  • 由于中斷處理一般在關(guān)閉中斷的情況下執(zhí)行,所以中斷處理不能太耗時,否則后續(xù)發(fā)生的中斷就不能實(shí)時地被處理。鑒于這個原因,Linux把中斷處理分為兩個部分, 上半部 和 下半部 , 上半部 在前面已經(jīng)介紹過,接下來就介紹一下 下半部 的執(zhí)行。

  • 一般中斷 上半部 只會做一些最基礎(chǔ)的操作(比如從網(wǎng)卡中復(fù)制數(shù)據(jù)到緩存中),然后對要執(zhí)行的中斷 下半部 進(jìn)行標(biāo)識,標(biāo)識完調(diào)用 do_softirq 函數(shù)進(jìn)行處理。

softirq機(jī)制

  • 中斷下半部 由 softirq(軟中斷) 機(jī)制來實(shí)現(xiàn)的,在Linux內(nèi)核中,有一個名為 softirq_vec 的數(shù)組,如下:

  • HI_SOFTIRQ 是高優(yōu)先級tasklet,而 TASKLET_SOFTIRQ 是普通tasklet,tasklet是基于softirq機(jī)制的一種任務(wù)隊列(下面會介紹)。 NET_TX_SOFTIRQ 和 NET_RX_SOFTIRQ 特定用于網(wǎng)絡(luò)子模塊的軟中斷(不作介紹)。

注冊softirq處理函數(shù)

  • 要注冊一個softirq處理函數(shù),可以通過 open_softirq 函數(shù)來進(jìn)行,代碼如下:

前面說了 softirq_vec 數(shù)組有32個元素,每個元素對應(yīng)一種類型的softirq,那么Linux怎么知道哪種softirq需要被執(zhí)行呢?在Linux中,每個CPU都有一個類型為 irq_cpustat_t 結(jié)構(gòu)的變量, irq_cpustat_t 結(jié)構(gòu)定義如下:

  • 其中 __softirq_active 字段表示有哪種softirq觸發(fā)了(int類型有32個位,每一個位代表一種softirq),而 __softirq_mask 字段表示哪種softirq被屏蔽了。Linux通過 __softirq_active 這個字段得知哪種softirq需要執(zhí)行(只需要把對應(yīng)位設(shè)置為1)。

  • 所以, do_softirq 函數(shù)首先通過 softirq_mask(cpu) 來獲取當(dāng)前CPU對應(yīng)被屏蔽的softirq,而 softirq_active(cpu) & mask 就是獲取需要執(zhí)行的softirq,然后就通過對比 __softirq_active 字段的各個位來判斷是否要執(zhí)行該類型的softirq。

tasklet機(jī)制

  • 前面說了,tasklet機(jī)制是基于softirq機(jī)制的,tasklet機(jī)制其實(shí)就是一個任務(wù)隊列,然后通過softirq執(zhí)行。在Linux內(nèi)核中有兩種tasklet,一種是高優(yōu)先級tasklet,一種是普通tasklet。這兩種tasklet的實(shí)現(xiàn)基本一致,唯一不同的就是執(zhí)行的優(yōu)先級,高優(yōu)先級tasklet會先于普通tasklet執(zhí)行。

  • tasklet本質(zhì)是一個隊列,通過結(jié)構(gòu)體 tasklet_head 存儲,并且每個CPU有一個這樣的隊列,我們來看看結(jié)構(gòu)體 tasklet_head 的定義:

從 tasklet_head 的定義可以知道, tasklet_head 結(jié)構(gòu)是 tasklet_struct 結(jié)構(gòu)隊列的頭部,而 tasklet_struct 結(jié)構(gòu)的 func 字段正式任務(wù)要執(zhí)行的函數(shù)指針。Linux定義了兩種的tasklet隊列,分別為 tasklet_vec 和 tasklet_hi_vec ,定義如下:

  • 可以看出, tasklet_vec 和 tasklet_hi_vec 都是數(shù)組,數(shù)組的元素個數(shù)為CPU的核心數(shù),也就是每個CPU核心都有一個高優(yōu)先級tasklet隊列和一個普通tasklet隊列。

調(diào)度tasklet

  • 如果我們有一個tasklet需要執(zhí)行,那么高優(yōu)先級tasklet可以通過 tasklet_hi_schedule 函數(shù)調(diào)度,而普通tasklet可以通過 tasklet_schedule 調(diào)度。這兩個函數(shù)基本一樣,所以我們只分析其中一個:

函數(shù)參數(shù)的類型是 tasklet_struct 結(jié)構(gòu)的指針,表示需要執(zhí)行的tasklet結(jié)構(gòu)。 tasklet_hi_schedule 函數(shù)首先判斷這個tasklet是否已經(jīng)被添加到隊列中,如果不是就添加到 tasklet_hi_vec 隊列中,并且通過調(diào)用 __cpu_raise_softirq(cpu, HI_SOFTIRQ) 來告訴softirq需要執(zhí)行 HI_SOFTIRQ 類型的softirq,我們來看看 __cpu_raise_softirq 函數(shù)的實(shí)現(xiàn):

  • 可以看出, __cpu_raise_softirq 函數(shù)就是把 irq_cpustat_t 結(jié)構(gòu)的 __softirq_active 字段的 nr位 設(shè)置為1。對于 tasklet_hi_schedule 函數(shù)就是把 HI_SOFTIRQ 位(0位)設(shè)置為1。

  • 前面我們也介紹過,Linux在初始化時會注冊兩種softirq, TASKLET_SOFTIRQ 和 HI_SOFTIRQ :

所以當(dāng)把 irq_cpustat_t 結(jié)構(gòu)的 __softirq_active 字段的 HI_SOFTIRQ 位(0位)設(shè)置為1時,softirq機(jī)制就會執(zhí)行 tasklet_hi_action 函數(shù),我們來看看 tasklet_hi_action 函數(shù)的實(shí)現(xiàn):

tasklet_hi_action 函數(shù)非常簡單,就是遍歷 tasklet_hi_vec 隊列并且執(zhí)行其中tasklet的處理函數(shù)。



一文完全讀懂--Linux內(nèi)核中斷處理的評論 (共 條)

分享到微博請遵守國家法律
林甸县| 永济市| 潜江市| 萨嘎县| 福安市| 肥东县| 新郑市| 江津市| 平利县| 苏尼特右旗| 增城市| 于都县| 西华县| 尼木县| 乃东县| 越西县| 乌恰县| 南通市| 山西省| 嘉兴市| 鱼台县| 邯郸县| 梅州市| 察哈| 尼勒克县| 昆明市| 武夷山市| 石景山区| 南康市| 汕尾市| 南漳县| 华池县| 龙陵县| 廉江市| 田阳县| 定陶县| 双鸭山市| 渭南市| 安仁县| 乡宁县| 子长县|