本文對中斷系統(tǒng)進(jìn)行了全面的分析與探討,主要包括中斷控制器、中斷分類、中斷親和力、中斷線程化與 SMP 中的中斷遷徙等。首先對中斷工作原理進(jìn)行了簡要分析,接著詳細(xì)探討了中斷親和力的實(shí)現(xiàn)原理,最后對中斷線程化與非線程化中斷之間的實(shí)現(xiàn)機(jī)理進(jìn)行了對比分析。
什么是中斷
Linux 內(nèi)核需要對連接到計(jì)算機(jī)上的所有硬件設(shè)備進(jìn)行管理,毫無疑問這是它的份內(nèi)事。如果要管理這些設(shè)備,首先得和它們互相通信才行,一般有兩種方案可實(shí)現(xiàn)這種功能:
- 輪詢(polling) 讓內(nèi)核定期對設(shè)備的狀態(tài)進(jìn)行查詢,然后做出相應(yīng)的處理;
- 中斷(interrupt) 讓硬件在需要的時(shí)候向內(nèi)核發(fā)出信號(變內(nèi)核主動為硬件主動)。
第一種方案會讓內(nèi)核做不少的無用功,因?yàn)檩喸兛倳芷谛缘闹貜?fù)執(zhí)行,大量地耗用 CPU 時(shí)間,因此效率及其低下,所以一般都是采用第二種方案 。 注釋 1
從物理學(xué)的角度看,中斷是一種電信號,由硬件設(shè)備產(chǎn)生,并直接送入中斷控制器(如 8259A)的輸入引腳上,然后再由中斷控制器向處理器發(fā)送相應(yīng)的信號。處理器一經(jīng)檢測到該信號,便中斷自己當(dāng)前正在處理的工作,轉(zhuǎn)而去處理中斷。此后,處理器會通知 OS 已經(jīng)產(chǎn)生中斷。這樣,OS 就可以對這個(gè)中斷進(jìn)行適當(dāng)?shù)奶幚?。不同的設(shè)備對應(yīng)的中斷不同,而每個(gè)中斷都通過一個(gè)唯一的數(shù)字標(biāo)識,這些值通常被稱為中斷請求線。
APIC vs 8259A
X86計(jì)算機(jī)的 CPU 為中斷只提供了兩條外接引腳:NMI 和 INTR。其中 NMI 是不可屏蔽中斷,它通常用于電源掉電和物理存儲器奇偶校驗(yàn);INTR是可屏蔽中斷,可以通過設(shè)置中斷屏蔽位來進(jìn)行中斷屏蔽,它主要用于接受外部硬件的中斷信號,這些信號由中斷控制器傳遞給 CPU。
常見的中斷控制器有兩種:
1. 可編程中斷控制器8259A
傳統(tǒng)的 PIC(Programmable Interrupt Controller)是由兩片 8259A 風(fēng)格的外部芯片以“級聯(lián)”的方式連接在一起。每個(gè)芯片可處理多達(dá) 8 個(gè)不同的 IRQ。因?yàn)閺?PIC 的 INT 輸出線連接到主 PIC 的 IRQ2 引腳,所以可用 IRQ 線的個(gè)數(shù)達(dá)到 15 個(gè),如圖 1 所示。
圖 1:8259A 級聯(lián)原理圖
2. 高級可編程中斷控制器(APIC)
8259A 只適合單 CPU 的情況,為了充分挖掘 SMP 體系結(jié)構(gòu)的并行性,能夠把中斷傳遞給系統(tǒng)中的每個(gè) CPU 至關(guān)重要?;诖死碛桑琁ntel 引入了一種名為 I/O 高級可編程控制器的新組件,來替代老式的 8259A 可編程中斷控制器。該組件包含兩大組成部分:一是“本地 APIC”,主要負(fù)責(zé)傳遞中斷信號到指定的處理器;舉例來說,一臺具有三個(gè)處理器的機(jī)器,則它必須相對的要有三個(gè)本地 APIC。另外一個(gè)重要的部分是 I/O APIC,主要是收集來自 I/O 裝置的 Interrupt 信號且在當(dāng)那些裝置需要中斷時(shí)發(fā)送信號到本地 APIC,系統(tǒng)中最多可擁有 8 個(gè) I/O APIC。
每個(gè)本地 APIC 都有 32 位的寄存器,一個(gè)內(nèi)部時(shí)鐘,一個(gè)本地定時(shí)設(shè)備以及為本地中斷保留的兩條額外的 IRQ 線 LINT0 和 LINT1。所有本地 APIC 都連接到 I/O APIC,形成一個(gè)多級 APIC 系統(tǒng),如圖 2 所示。
圖 2:多級I/O APIC系統(tǒng)
目前大部分單處理器系統(tǒng)都包含一個(gè) 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 接收。
辨別一個(gè)系統(tǒng)是否正在使用 I/O APIC,可以在命令行輸入如下命令:
# cat /proc/interrupts
CPU0
0: 90504 IO-APIC-edge timer
1: 131 IO-APIC-edge i8042
8: 4 IO-APIC-edge rtc
9: 0 IO-APIC-level acpi
12: 111 IO-APIC-edge i8042
14: 1862 IO-APIC-edge ide0
15: 28 IO-APIC-edge ide1
177: 9 IO-APIC-level eth0
185: 0 IO-APIC-level via82cxxx
...
|
如果輸出結(jié)果中列出了 IO-APIC,說明您的系統(tǒng)正在使用 APIC。如果看到 XT-PIC,意味著您的系統(tǒng)正在使用 8259A 芯片。
中斷分類
中斷可分為同步(synchronous)中斷和異步(asynchronous)中斷:
1. 同步中斷是當(dāng)指令執(zhí)行時(shí)由 CPU 控制單元產(chǎn)生,之所以稱為同步,是因?yàn)橹挥性谝粭l指令執(zhí)行完畢后 CPU 才會發(fā)出中斷,而不是發(fā)生在代碼指令執(zhí)行期間,比如系統(tǒng)調(diào)用。
2. 異步中斷是指由其他硬件設(shè)備依照 CPU 時(shí)鐘信號隨機(jī)產(chǎn)生,即意味著中斷能夠在指令之間發(fā)生,例如鍵盤中斷。
根據(jù) Intel 官方資料,同步中斷稱為異常(exception),異步中斷被稱為中斷(interrupt)。
中斷可分為可屏蔽中斷(Maskable interrupt)和非屏蔽中斷(Nomaskable interrupt)。異??煞譃楣收希?em>fault)、陷阱(trap)、終止(abort)三類。
從廣義上講,中斷可分為四類:中斷、故障、陷阱、終止。這些類別之間的異同點(diǎn)請參看 表 1。
表 1:中斷類別及其行為
類別 |
原因 |
異步/同步 |
返回行為 |
中斷 |
來自I/O設(shè)備的信號 |
異步 |
總是返回到下一條指令 |
陷阱 |
有意的異常 |
同步 |
總是返回到下一條指令 |
故障 |
潛在可恢復(fù)的錯(cuò)誤 |
同步 |
返回到當(dāng)前指令 |
終止 |
不可恢復(fù)的錯(cuò)誤 |
同步 |
不會返回 |
X86 體系結(jié)構(gòu)的每個(gè)中斷都被賦予一個(gè)唯一的編號或者向量(8 位無符號整數(shù))。非屏蔽中斷和異常向量是固定的,而可屏蔽中斷向量可以通過對中斷控制器的編程來改變。
Linux 2.6 中斷處理原理簡介
中斷描述符表(Interrupt Descriptor Table,IDT)是一個(gè)系統(tǒng)表,它與每一個(gè)中斷或異常向量相聯(lián)系,每一個(gè)向量在表中存放的是相應(yīng)的中斷或異常處理程序的入口地址。內(nèi)核在允許中斷發(fā)生前,也就是在系統(tǒng)初始化時(shí),必須把 IDT 表的初始化地址裝載到 idtr 寄存器中,初始化表中的每一項(xiàng)。
當(dāng)處于實(shí)模式下時(shí),IDT 被初始化并由 BIOS 程序所使用。然而,一旦 Linux 開始接管,IDT 就被移到 ARM 的另一個(gè)區(qū)域,并進(jìn)行第二次初始化,因?yàn)?Linux 不使用任何 BIOS 程序,而使用自己專門的中斷服務(wù)程序(例程)(interrupt service routine,ISR)。中斷和異常處理程序很像常規(guī)的 C 函數(shù)
有三個(gè)主要的數(shù)據(jù)結(jié)構(gòu)包含了與 IRQ 相關(guān)的所有信息:hw_interrupt_type 、irq_desc_t 和 irqaction ,圖3 解釋了它們之間是如何關(guān)聯(lián)的。
圖 3:IRQ 結(jié)構(gòu)之間的關(guān)系
在 X86 系統(tǒng)中,對于 8259A 和 I/O APIC 這兩種不同類型的中斷控制器,hw_interrupt_type 結(jié)構(gòu)體被賦予不同的值,具體區(qū)別參見表 2。
表 2:8259A 和 I/O APIC PIC 的區(qū)別
8259A |
I/O APIC |
static struct hw_interrupt_type i8259A_irq_type = { "XT-PIC", startup_8259A_irq, shutdown_8259A_irq, enable_8259A_irq, disable_8259A_irq, mask_and_ack_8259A, end_8259A_irq, NULL }; |
static struct hw_interrupt_type ioapic_edge_type = { .typename = "IO-APIC-edge", .startup = startup_edge_ioapic, .shutdown = shutdown_edge_ioapic, .enable = enable_edge_ioapic, .disable = disable_edge_ioapic, .ack = ack_edge_ioapic, .end = end_edge_ioapic, .set_affinity = set_ioapic_affinity, }; static struct hw_interrupt_type ioapic_level_type = { .typename = "IO-APIC-level", .startup = startup_level_ioapic, .shutdown = shutdown_level_ioapic, .enable = enable_level_ioapic, .disable = disable_level_ioapic, .ack = mask_and_ack_level_ioapic, .end = end_level_ioapic, .set_affinity = set_ioapic_affinity, }; |
在中斷初始化階段,調(diào)用 hw_interrupt_type 類型的變量初始化 irq_desc_t 結(jié)構(gòu)中的 handle 成員。在早期的系統(tǒng)中使用級聯(lián)的8259A,所以將用 i8259A_irq_type 來進(jìn)行初始化,而對于SMP系統(tǒng)來說,要么以 ioapic_edge_type ,或以 ioapic_level_type 來初始化 handle 變量。
對于每一個(gè)外設(shè),要么以靜態(tài)(聲明為 static 類型的全局變量)或動態(tài)(調(diào)用 request_irq 函數(shù))的方式向 Linux 內(nèi)核注冊中斷處理程序。不管以何種方式注冊,都會聲明或分配一塊 irqaction 結(jié)構(gòu)(其中 handler 指向中斷服務(wù)程序),然后調(diào)用 setup_irq() 函數(shù),將 irq_desc_t 和 irqaction 聯(lián)系起來。
當(dāng)中斷發(fā)生時(shí),通過中斷描述符表 IDT 獲取中斷服務(wù)程序入口地址,對于 32≤ i ≤255(i≠128) 之間的中斷向量,將會執(zhí)行 push $i-256,jmp common_interrupt 指令。隨之將調(diào)用 do_IRQ() 函數(shù),以中斷向量為 irq_desc[] 結(jié)構(gòu)的下標(biāo),獲取 action 的指針,然后調(diào)用 handler 所指向的中斷服務(wù)程序。
從以上描述,我們不難看出整個(gè)中斷的流程,如圖 4 所示:
圖 4:X86中斷流
本文作者之一曾經(jīng)對2.6.10的中斷系統(tǒng)進(jìn)行過情景分析,有興趣的讀者可以和作者取得聯(lián)系,獲取相關(guān)資料。
中斷綁定——中斷親和力(IRQ Affinity)
在 SMP 體系結(jié)構(gòu)中,我們可以通過調(diào)用系統(tǒng)調(diào)用和一組相關(guān)的宏來設(shè)置 CPU 親和力(CPU affinity),將一個(gè)或多個(gè)進(jìn)程綁定到一個(gè)或多個(gè)處理器上運(yùn)行。中斷在這方面也毫不示弱,也具有相同的特性。中斷親和力是指將一個(gè)或多個(gè)中斷源綁定到特定的 CPU 上運(yùn)行。中斷親和力最初由 Ingo Molnar 設(shè)計(jì)并實(shí)現(xiàn)。
在 /proc/irq 目錄中,對于已經(jīng)注冊中斷處理程序的硬件設(shè)備,都會在該目錄下存在一個(gè)以該中斷號命名的目錄 IRQ# ,IRQ# 目錄下有一個(gè) smp_affinity 文件(SMP 體系結(jié)構(gòu)才有該文件),它是一個(gè) CPU 的位掩碼,可以用來設(shè)置該中斷的親和力, 默認(rèn)值為 0xffffffff ,表明把中斷發(fā)送到所有的 CPU 上去處理。如果中斷控制器不支持 IRQ affinity ,不能改變此默認(rèn)值,同時(shí)也不能關(guān)閉所有的 CPU 位掩碼,即不能設(shè)置成 0x0 。
我們以網(wǎng)卡(eth1,中斷號 44 )為例,在具有 8 個(gè) CPU 的服務(wù)器上來設(shè)置網(wǎng)卡中斷的親和力(以下數(shù)據(jù)出自內(nèi)核源碼 Documentation\IRQ-affinity.txt ):
[root@moon 44]# cat smp_affinity
ffffffff
[root@moon 44]# echo 0f > smp_affinity
[root@moon 44]# cat smp_affinity
0000000f
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
...
--- hell ping statistics ---
6029 packets transmitted, 6027 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.1/0.4 ms
[root@moon 44]# cat /proc/interrupts | grep 44:
44: 0 1785 1785 1783 1783 1 1 0 IO-APIC-level eth1
[root@moon 44]# echo f0 > smp_affinity
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
..
--- hell ping statistics ---
2779 packets transmitted, 2777 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.5/585.4 ms
[root@moon 44]# cat /proc/interrupts | grep 44:
44: 1068 1785 1785 1784 1784 1069 1070 1069 IO-APIC-level eth1
[root@moon 44]#
|
在上例中,我們首先只允許在 CPU0~3 上處理網(wǎng)卡中斷,接著運(yùn)行 ping 程序,不難發(fā)現(xiàn)在 CPU4~7 上并沒有對網(wǎng)卡中斷進(jìn)行處理。然后只在 CPU4~7 上對網(wǎng)卡中斷進(jìn)行處理, CPU0~3 不對網(wǎng)卡中斷進(jìn)行任何處理,運(yùn)行 ping 程序之后,再次查看 /proc/interrupts 文件時(shí),不難發(fā)現(xiàn) CPU4~7 上的中斷次數(shù)明顯增加,而 CPU0~3 上的中斷次數(shù)沒有太大的變化。
在探討中斷親和力的實(shí)現(xiàn)原理之前,我們首先來了解 I/O APIC 中的組成。
I/O APIC 由一組 24 條 IRQ 線,一張 24 項(xiàng)的中斷重定向表(Interrupt Redirection Table),可編程寄存器,以及通過 APIC 總線發(fā)送和接收 APIC 信息的一個(gè)信息單元組成。其中與中斷親和力息息相關(guān)的是中斷重定向表, 中斷重定向表表中的每一項(xiàng)都可以被單獨(dú)編程以指明中斷向量和優(yōu)先級、目標(biāo)處理器及選擇處理器的方式 。
通過表 2,不難發(fā)現(xiàn) 8259A 和 APIC 中斷控制器最大不同點(diǎn)在于 hw_interrupt_type 類型變量的最后一項(xiàng)。對于 8259A 類型,set_affinity 被置為 NULL ,而對于 SMP 的 APIC 類型,set_affinity 被賦值為 set_ioapic_affinity 。
在系統(tǒng)初始化期間,對于 SMP 體系結(jié)構(gòu),將會調(diào)用 setup_IO_APIC_irqs() 函數(shù)來初始化 I/O APIC 芯片,芯片中的中斷重定向表的 24 項(xiàng)被填充。在系統(tǒng)啟動期間,所有的 CPU 都執(zhí)行 setup_local_APIC() 函數(shù),完成本地的 APIC 初始化。當(dāng)有中斷被觸發(fā)時(shí),將相應(yīng)的中斷重定向表中的值轉(zhuǎn)換成一條消息,然后,通過 APIC 總線把消息發(fā)送給一個(gè)或多個(gè)本地 APIC 單元,這樣,中斷就能立即被傳遞給一個(gè)特定的 CPU,或一組 CPU,或所有的 CPU,從而來實(shí)現(xiàn)中斷親和力。
當(dāng)我們通過 cat 命令將 CPU 掩碼寫進(jìn) smp_affinity 文件時(shí),此時(shí)的調(diào)用路線圖為:write() ->sys_write() ->vfs_write() ->proc_file_write() ->irq_affinity_write_proc() ->set_affinity() ->set_ioapic_affinity() ->set_ioapic_affinity_irq() ->io_apic_write() ;其中在調(diào)用 set_ioapic_affinity_irq() 函數(shù)時(shí),以中斷號和 CPU 掩碼作為參數(shù),接著繼續(xù)調(diào)用 io_apic_write() ,修改相應(yīng)的中斷重定向中的值,來完成中斷親和力的設(shè)置。當(dāng)執(zhí)行 ping 命令時(shí),網(wǎng)卡中斷被觸發(fā),產(chǎn)生了一個(gè)中斷信號,多 APIC 系統(tǒng)根據(jù)中斷重定向表中的值,依照仲裁機(jī)制,選擇 CPU0~3 中的某一個(gè) CPU,并將該信號傳遞給相應(yīng)的本地 APIC,本地 APIC 又中斷它的 CPU,整個(gè)事件不通報(bào)給其他所有的 CPU。
新特性展望——中斷線程化(Interrupt Threads)
在嵌入式領(lǐng)域,業(yè)界對 Linux 實(shí)時(shí)性的呼聲越來越高,對中斷進(jìn)行改造勢在必行。在 Linux 中,中斷具有最高的優(yōu)先級。不論在任何時(shí)刻,只要產(chǎn)生中斷事件,內(nèi)核將立即執(zhí)行相應(yīng)的中斷處理程序,等到所有掛起的中斷和軟中斷處理完畢后才能執(zhí)行正常的任務(wù),因此有可能造成實(shí)時(shí)任務(wù)得不到及時(shí)的處理。中斷線程化之后,中斷將作為內(nèi)核線程運(yùn)行而且被賦予不同的實(shí)時(shí)優(yōu)先級,實(shí)時(shí)任務(wù)可以有比中斷線程更高的優(yōu)先級。這樣,具有最高優(yōu)先級的實(shí)時(shí)任務(wù)就能得到優(yōu)先處理,即使在嚴(yán)重負(fù)載下仍有實(shí)時(shí)性保證。
目前較新的 Linux 2.6.17 還不支持中斷線程化。但由 Ingo Molnar 設(shè)計(jì)并實(shí)現(xiàn)的實(shí)時(shí)補(bǔ)丁,實(shí)現(xiàn)了中斷線程化。最新的下載地址為:
http://people./~mingo/realtime-preempt/patch-2.6.17-rt9
下面將對中斷線程化進(jìn)行簡要分析。
在初始化階段,中斷線程化的中斷初始化與常規(guī)中斷初始化大體上相同,在 start_kernel() 函數(shù)中都調(diào)用了 trap_init() 和 init_IRQ() 兩個(gè)函數(shù)來初始化 irq_desc_t 結(jié)構(gòu)體,不同點(diǎn)主要體現(xiàn)在內(nèi)核初始化創(chuàng)建 init 線程時(shí),中斷線程化的中斷在 init() 函數(shù)中還將調(diào)用 init_hardirqs(kernel/irq/manage.c (已經(jīng)打過上文提到的補(bǔ)丁)),來為每一個(gè) IRQ 創(chuàng)建一個(gè)內(nèi)核線程,最高實(shí)時(shí)優(yōu)先級為 50,依次類推直到 25,因此任何 IRQ 線程的最低實(shí)時(shí)優(yōu)先級為 25。
void __init init_hardirqs(void)
{
……
for (i = 0; i < NR_IRQS; i++) {
irq_desc_t *desc = irq_desc + i;
if (desc->action && !(desc->status & IRQ_NODELAY))
desc->thread = kthread_create(do_irqd, desc, "IRQ %d", irq);
……
}
}
static int do_irqd(void * __desc)
{
……
/*
* Scale irq thread priorities from prio 50 to prio 25
*/
param.sched_priority = curr_irq_prio;
if (param.sched_priority > 25)
curr_irq_prio = param.sched_priority - 1;
……
}
|
如果某個(gè)中斷號狀態(tài)位中的 IRQ_NODELAY 被置位,那么該中斷不能被線程化。
在中斷處理階段,兩者之間的異同點(diǎn)主要體現(xiàn)在:兩者相同的部分是當(dāng)發(fā)生中斷時(shí),CPU 將調(diào)用 do_IRQ() 函數(shù)來處理相應(yīng)的中斷,do_IRQ() 在做了必要的相關(guān)處理之后調(diào)用 __do_IRQ() 。兩者最大的不同點(diǎn)體現(xiàn)在 __do_IRQ() 函數(shù)中,在該函數(shù)中,將判斷該中斷是否已經(jīng)被線程化(如果中斷描述符的狀態(tài)字段不包含 IRQ_NODELAY 標(biāo)志,則說明該中斷被線程化了),對于沒有線程化的中斷,將直接調(diào)用 handle_IRQ_event() 函數(shù)來處理。
fastcall notrace unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
{
……
if (redirect_hardirq(desc))
goto out_no_end;
……
action_ret = handle_IRQ_event(irq, regs, action);
……
}
int redirect_hardirq(struct irq_desc *desc)
{
……
if (!hardirq_preemption || (desc->status & IRQ_NODELAY) || !desc->thread)
return 0;
……
if (desc->thread && desc->thread->state != TASK_RUNNING)
wake_up_process(desc->thread);
……
}
|
對于已經(jīng)線程化的情況,調(diào)用 wake_up_process() 函數(shù)喚醒中斷處理線程,并開始運(yùn)行,內(nèi)核線程將調(diào)用 do_hardirq() 來處理相應(yīng)的中斷,該函數(shù)將判斷是否有中斷需要被處理,如果有就調(diào)用 handle_IRQ_event() 來處理。handle_IRQ_event() 將直接調(diào)用相應(yīng)的中斷處理函數(shù)來完成中斷處理。
不難看出,不管是線程化還是非線程化的中斷,最終都會執(zhí)行 handle_IRQ_event() 函數(shù)來調(diào)用相應(yīng)的中斷處理函數(shù),只是線程化的中斷處理函數(shù)是在內(nèi)核線程中執(zhí)行的。
并不是所有的中斷都可以被線程化,比如時(shí)鐘中斷,主要用來維護(hù)系統(tǒng)時(shí)間以及定時(shí)器等,其中定時(shí)器是操作系統(tǒng)的脈搏,一旦被線程化,就有可能被掛起,這樣后果將不堪設(shè)想,所以不應(yīng)當(dāng)被線程化。如果某個(gè)中斷需要被實(shí)時(shí)處理,它可以像時(shí)鐘中斷那樣,用 SA_NODELAY 標(biāo)志來聲明自己非線程化,例如:
static struct irqaction irq0 = {
timer_interrupt, SA_INTERRUPT | SA_NODELAY, CPU_MASK_NONE, "timer", NULL, NULL
};
|
其中,SA_NODELAY 到 IRQ_NODELAY 之間的轉(zhuǎn)換,是在 setup_irq() 函數(shù)中完成的。
中斷負(fù)載均衡—SMP體系結(jié)構(gòu)下的中斷
中斷負(fù)載均衡的實(shí)現(xiàn)主要封裝在 arch\ arch\i386\kernel\io-apic.c 文件中。如果在編譯內(nèi)核時(shí)配置了 CONFIG_IRQBALANCE 選項(xiàng),那么 SMP 體系結(jié)構(gòu)中的中斷負(fù)載均衡將以模塊的形式存在于內(nèi)核中。
late_initcall(balanced_irq_init);
#define late_initcall(fn) module_init(fn) //include\linux\init.h
|
在 balanced_irq_init() 函數(shù)中,將創(chuàng)建一個(gè)內(nèi)核線程來負(fù)責(zé)中斷負(fù)載均衡:
static int __init balanced_irq_init(void)
{ ……
printk(KERN_INFO "Starting balanced_irq\n");
if (kernel_thread(balanced_irq, NULL, CLONE_KERNEL) >= 0)
return 0;
else
printk(KERN_ERR "balanced_irq_init: failed to spawn balanced_irq");
……
}
|
在 balanced_irq() 函數(shù)中,每隔 5HZ=5s 的時(shí)間,將調(diào)用一次 do_irq_balance() 函數(shù),進(jìn)行中斷的遷徙。將重負(fù)載 CPU 上的中斷遷移到較空閑的CPU上進(jìn)行處理。
總結(jié)
隨著中斷親和力和中斷線程化的相繼實(shí)現(xiàn),Linux 內(nèi)核在 SMP 和實(shí)時(shí)性能方面的表現(xiàn)越來越讓人滿意,完全有理由相信,在不久的將來,中斷線程化將被合并到基線版本中。本文對中斷線程化的分析只是起一個(gè)拋磚引玉的作用,當(dāng)新特性發(fā)布時(shí),不至于讓人感到迷茫。
參考資料
- Rebert Love,《Linux Kernel Development,2rd Edition》,機(jī)械工業(yè)出版社,2006。
- Daniel P. Bovet,Marco Cesati,《Understanding the Linux Kernel,3rd Edition》,東南大學(xué)出版社,2006。
- Jonatban Corbet 等,魏永明等譯,《Linux設(shè)備驅(qū)動程序》,中國電力出版社,2006。
- Gordon Fischer 等,《The Linux Kernel Prime》,機(jī)械工業(yè)出版社,2006。
|