1. 程式人生 > >linux 核心 中斷親和力-affinity

linux 核心 中斷親和力-affinity

中斷繫結——中斷親和力(IRQ Affinity)

在 SMP 體系結構中,我們可以通過呼叫系統呼叫和一組相關的巨集來設定 CPU 親和力(CPU affinity),將一個或多個程序繫結到一個或多個處理器上執行。中斷在這方面也毫不示弱,也具有相同的特性。中斷親和力是指將一個或多箇中斷源繫結到特定的 CPU 上執行。中斷親和力最初由 Ingo Molnar 設計並實現。

在 /proc/irq 目錄中,對於已經註冊中斷處理程式的硬體裝置,都會在該目錄下存在一個以該中斷號命名的目錄 IRQ# ,IRQ# 目錄下有一個 smp_affinity 檔案(SMP 體系結構才有該檔案),它是一個 CPU 的位掩碼,可以用來設定該中斷的親和力, 預設值為 0xffffffff,表明把中斷髮送到所有的 CPU 上去處理。如果中斷控制器不支援 IRQ affinity,不能改變此預設值,同時也不能關閉所有的 CPU 位掩碼,即不能設定成 0x0。

我們以網絡卡(eth1,中斷號 44 )為例,在具有 8 個 CPU 的伺服器上來設定網絡卡中斷的親和力(以下資料出自核心原始碼 Documentation\IRQ-affinity.txt):

[[email protected] 44]# cat smp_affinity

ffffffff

[[email protected] 44]# echo 0f > smp_affinity

[[email protected] 44]# cat smp_affinity

0000000f

[[email protected] 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

[[email protected] 44]# cat /proc/interrupts | grep 44:

44:   0   1785   1785   1783   1783   1   1   0   IO-APIC-level   eth1

[[email protected] 44]# echo f0 > smp_affinity

[[email protected] 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

[[email protected] 44]# cat /proc/interrupts | grep 44:

44: 1068 1785 1785 1784   1784   1069   1070   1069   IO-APIC-level eth1

[[email protected] 44]#    

在上例中,我們首先只允許在 CPU0~3 上處理網絡卡中斷,接著執行 ping 程式,不難發現在 CPU4~7 上並沒有對網絡卡中斷進行處理。然後只在 CPU4~7 上對網絡卡中斷進行處理, CPU0~3 不對網絡卡中斷進行任何處理,執行 ping 程式之後,再次檢視 /proc/interrupts 檔案時,不難發現 CPU4~7 上的中斷次數明顯增加,而 CPU0~3 上的中斷次數沒有太大的變化。

在探討中斷親和力的實現原理之前,我們首先來了解 I/O APIC 中的組成。

I/O APIC 由一組 24 條 IRQ 線,一張 24 項的中斷重定向表(Interrupt Redirection Table),可程式設計暫存器,以及通過 APIC 匯流排傳送和接收 APIC 資訊的一個資訊單元組成。其中與中斷親和力息息相關的是中斷重定向表, 中斷重定向表表中的每一項都可以被單獨程式設計以指明中斷向量和優先順序、目標處理器及選擇處理器的方式 。

通過表 2,不難發現 8259A 和 APIC 中斷控制器最大不同點在於 hw_interrupt_type 型別變數的最後一項。對於 8259A 型別,set_affinity 被置為 NULL,而對於 SMP 的 APIC 型別,set_affinity 被賦值為 set_ioapic_affinity。

在系統初始化期間,對於 SMP 體系結構,將會呼叫 setup_IO_APIC_irqs() 函式來初始化 I/O APIC 晶片,晶片中的中斷重定向表的 24 項被填充。在系統啟動期間,所有的 CPU 都執行 setup_local_APIC() 函式,完成本地的 APIC 初始化。當有中斷被觸發時,將相應的中斷重定向表中的值轉換成一條訊息,然後,通過 APIC 匯流排把訊息傳送給一個或多個本地 APIC 單元,這樣,中斷就能立即被傳遞給一個特定的 CPU,或一組 CPU,或所有的 CPU,從而來實現中斷親和力。

當我們通過 cat 命令將 CPU 掩碼寫進 smp_affinity 檔案時,此時的呼叫路線圖為:write() ->sys_write() ->vfs_write() ->proc_file_write() ->irq_affinity_write_proc() ->set_affinity() ->set_ioapic_affinity() ->set_ioapic_affinity_irq() ->io_apic_write();其中在呼叫 set_ioapic_affinity_irq() 函式時,以中斷號和 CPU 掩碼作為引數,接著繼續呼叫 io_apic_write(),修改相應的中斷重定向中的值,來完成中斷親和力的設定。當執行 ping 命令時,網絡卡中斷被觸發,產生了一箇中斷訊號,多 APIC 系統根據中斷重定向表中的值,依照仲裁機制,選擇 CPU0~3 中的某一個 CPU,並將該訊號傳遞給相應的本地 APIC,本地 APIC 又中斷它的 CPU,整個事件不通報給其他所有的 CPU。

新特性展望——中斷執行緒化(Interrupt Threads)

在嵌入式領域,業界對 Linux 實時性的呼聲越來越高,對中斷進行改造勢在必行。在 Linux 中,中斷具有最高的優先順序。不論在任何時刻,只要產生中斷事件,核心將立即執行相應的中斷處理程式,等到所有掛起的中斷和軟中斷處理完畢後才能執行正常的任務,因此有可能造成實時任務得不到及時的處理。中斷執行緒化之後,中斷將作為核心執行緒執行而且被賦予不同的實時優先順序,實時任務可以有比中斷執行緒更高的優先順序。這樣,具有最高優先順序的實時任務就能得到優先處理,即使在嚴重負載下仍有實時性保證。

目前較新的 Linux 2.6.17 還不支援中斷執行緒化。但由 Ingo Molnar 設計並實現的實時補丁,實現了中斷執行緒化。最新的下載地址為:

下面將對中斷執行緒化進行簡要分析。

在初始化階段,中斷執行緒化的中斷初始化與常規中斷初始化大體上相同,在 start_kernel() 函式中都呼叫了 trap_init() 和 init_IRQ() 兩個函式來初始化 irq_desc_t 結構體,不同點主要體現在核心初始化建立 init 執行緒時,中斷執行緒化的中斷在 init() 函式中還將呼叫 init_hardirqs(kernel/irq/manage.c(已經打過上文提到的補丁)),來為每一個 IRQ 建立一個核心執行緒,最高實時優先順序為 50,依次類推直到 25,因此任何 IRQ 執行緒的最低實時優先順序為 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;

   ……

}
如果某個中斷號狀態位中的 IRQ_NODELAY 被置位,那麼該中斷不能被執行緒化。

在中斷處理階段,兩者之間的異同點主要體現在:兩者相同的部分是當發生中斷時,CPU 將呼叫 do_IRQ() 函式來處理相應的中斷,do_IRQ() 在做了必要的相關處理之後呼叫 __do_IRQ()。兩者最大的不同點體現在 __do_IRQ() 函式中,在該函式中,將判斷該中斷是否已經被執行緒化(如果中斷描述符的狀態欄位不包含 IRQ_NODELAY 標誌,則說明該中斷被執行緒化了),對於沒有執行緒化的中斷,將直接呼叫 handle_IRQ_event() 函式來處理。

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);

……

}

對於已經執行緒化的情況,呼叫 wake_up_process() 函式喚醒中斷處理執行緒,並開始執行,核心執行緒將呼叫 do_hardirq() 來處理相應的中斷,該函式將判斷是否有中斷需要被處理,如果有就呼叫 handle_IRQ_event() 來處理。handle_IRQ_event() 將直接呼叫相應的中斷處理函式來完成中斷處理。

不難看出,不管是執行緒化還是非執行緒化的中斷,最終都會執行 handle_IRQ_event() 函式來呼叫相應的中斷處理函式,只是執行緒化的中斷處理函式是在核心執行緒中執行的。

並不是所有的中斷都可以被執行緒化,比如時鐘中斷,主要用來維護系統時間以及定時器等,其中定時器是作業系統的脈搏,一旦被執行緒化,就有可能被掛起,這樣後果將不堪設想,所以不應當被執行緒化。如果某個中斷需要被實時處理,它可以像時鐘中斷那樣,用 SA_NODELAY 標誌來宣告自己非執行緒化,例如:

static struct irqaction irq0 = {

          timer_interrupt, SA_INTERRUPT | SA_NODELAY, CPU_MASK_NONE, "timer", NULL, NULL

};

其中,SA_NODELAY 到 IRQ_NODELAY 之間的轉換,是在 setup_irq() 函式中完成的。


中斷負載均衡—SMP體系結構下的中斷

中斷負載均衡的實現主要封裝在 arch\ arch\i386\kernel\io-apic.c 檔案中。如果在編譯核心時配置了 CONFIG_IRQBALANCE 選項,那麼 SMP 體系結構中的中斷負載均衡將以模組的形式存在於核心中。

late_initcall(balanced_irq_init);

#define late_initcall(fn)                module_init(fn) //include\linux\init.h

在 balanced_irq_init() 函式中,將建立一個核心執行緒來負責中斷負載均衡:

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() 函式中,每隔 5HZ=5s 的時間,將呼叫一次 do_irq_balance() 函式,進行中斷的遷徙。將重負載 CPU 上的中斷遷移到較空閒的CPU上進行處理。

隨著中斷親和力和中斷執行緒化的相繼實現,Linux 核心在 SMP 和實時效能方面的表現越來越讓人滿意,完全有理由相信,在不久的將來,中斷執行緒化將被合併到基線版本中。本文對中斷執行緒化的分析只是起一個拋磚引玉的作用,當新特性發布時,不至於讓人感到迷茫。


相關推薦

linux 核心 中斷親和力-affinity

中斷繫結——中斷親和力(IRQ Affinity) 在 SMP 體系結構中,我們可以通過呼叫系統呼叫和一組相關的巨集來設定 CPU 親和力(CPU affinity),將一個或多個程序繫結到一個或多個處理器上執行。中斷在這方面也毫不示弱,也具有相同的特性。中斷親和力是指將一個或多箇中斷源繫結到特定的 CP

Linux核心中斷引入使用者空間(非同步通知機制)

當linux核心空間發生中斷後怎麼使使用者空間的應用程式執行相應的函式呢,當晶片有資料到來時核心會產生一箇中斷,但是怎樣通知應用程式來取資料,以前這個問題一直困擾我很長時間,後來發現linux中有非同步通知機制,在使用者程式中用signal註冊一個響應SIGIO訊號的回撥函式,然後在驅動程式中向該程

深入理解 Linux 核心---中斷和異常

中斷或異常會改變處理器執行的指令順序。 異常: 來源:CPU 控制單元, 時機:只有在一條指令終止執行後 CPU 才會發出中斷。 原因:程式產生錯誤,或核心必須處理的異常條件。 中斷: 來源:間隔定時器或 I/O 裝置。 時機:隨機產生。 原因:依照 CP

linux核心中斷分析

知識要點 一、struct irq_chip、struct irq_desc[]、struct irqaction三者之間的關係 二、Linux核心中中斷的初始化流程、中斷的註冊流程、中斷的執行流程 三、多核cpu的中斷親和力和中斷負載均衡 四、中斷的上半部和下半部 一、s

linux核心-中斷處理程式

中斷共享  中斷共享是指多個裝置共享一根中斷線的情況  中斷共享的使用方法:  (1).在申請中斷時,使用IRQF_SHARED標識  (2).在中斷到來時,會遍歷共享此中斷的所有中斷處理程式,直到某一個函式返回  IRQ_HANDLED,在中斷處理程式頂半部中,應迅速根據硬體暫存器中的資訊參照dev_id引

linux 核心中斷trace機制使用

如果要開啟Linux核心的中斷trace機制,需要在config裡面,開啟如下選項: CONFIG_IRQSOFF_TRACER=y 並且需要重新編譯核心。 然後,依次輸入以下命令: echo 0 > /sys/kernel/debug/tracing/trac

LINUX-核心-中斷分析-中斷向量表(2)-mips

mips中斷概念 在《MIPS體系結構透視》的第5章說到,在MIPS中,中斷、陷阱、系統呼叫和任何可以中斷程式正常執行流的情況全被都被稱為異常。 以上這種統一到“異常”的概念及其邏輯當然會體現在MIPS的異常入口點的設計中,特別如MIPS中斷入口點的引出。

Linux核心中斷和異常分析(上)

中斷,通常被定義為一個事件。打個比方,你燒熱水,水沸騰了,這時候你要去關掉燒熱水的電磁爐,然後再去辦之前手中停不下來的事情。那麼熱水沸騰就是打斷你正常工作的一個訊號機制。當然,還有其它的情況,我們以後

Linux核心中斷機制(一):中斷註冊方法

今天在網上看到一份不錯的講解核心中斷原理的文章,分享給大家! 1.中斷註冊方法 在 linux 核心中用於申請中斷的函式是 request_irq(),函式原型在Kernel/irq/manage.c 中定義: int request_irq(unsigned int

Linux核心中斷機制(三):中斷處理上

核心中斷處理過程 S3C2410和Linux2.6.26核心為例講解處理過程 1.中斷向量表arch\arm\kernel\entry-armv.S __vectors_start: swi SYS_ERROR0 b vector_und + stubs_offset l

【轉】對Linux核心中程序上下文和中斷上下文的理解

轉自:http://www.embedu.org/Column/Column240.htm 核心空間和使用者空間是作業系統理論的基礎之一,即核心功能模組執行在核心空間,而應用程式執行在使用者空間。現代的CPU都具有不同的操作模式,代表不同的級別,不同的級別具有不同的功能,在較低的級別中將禁止某些

例項:tasklet實現軟中斷(學習《Linux核心設計與實現》記錄)

tasklet是通過軟中斷實現的,tasklet本身也是軟中斷。 關於tasklet更詳細的知識,還是建議看一下《Linux核心設計與實現》 本貼子只介紹一下具體的流程。 驅動程式原始碼: #include <linux/init.h> #include <linu

9.Linux核心設計與實現 P91---中斷中斷處理程式 (轉)

      中斷還是中斷,我講了很多次的中斷了,今天還是要講中斷,為啥呢?因為在作業系統中,中斷是必須要講的..       那麼什麼叫中斷呢, 中斷還是打斷,這樣一說你就不明白了。唉,中斷還真是有點像打斷。我們知道linux管理所有的硬體裝置,要做的第一件事先是通訊。然後,我們天天在說一句話:處理器的速度跟

linux核心自旋鎖和中斷知識講解

一、併發與競態三個要點 1、只要併發的執行單元同時訪問共享記憶體是就會出現競態 2、解決競態的唯一途徑是保證共享資源的互斥訪問,即一個執行單元在訪問共享資源時,其他的執行單元被禁止訪問。 3、

Linux核心中斷、軟中斷、tasklet

http://blog.csdn.net/jansonzhe/article/details/48786207 在之前我所寫的Linux驅動程式中,會經常使用到中斷機制,像CC1100高頻驅動、倒車雷達驅動等等。但所用到的中斷機制都基本上是用到中斷的頂半部,即:編寫中斷

Linux核心之禁止中斷和禁止核心搶佔

禁止中斷指的是Linux核心停工了一組介面用於操作機器上的中斷狀態。這些介面為我們提供了能夠禁止當前處理器的中斷系統,或者遮蔽掉整個機器的一條中斷線的能力。通過禁止中斷,可以確保某個中斷處理程式不會搶佔當前的程式碼。控制中斷系統在Linux的實現有很多,以local_irq

把握linux核心設計思想(六):核心時鐘中斷

(位於檔案kernel/time/tick-common.c)void __init tick_init(void) { clockevents_register_notifier(&tick_notifier); } tick_notifier定義如下:static struct notif

linux核心分析之缺頁中斷

linux缺頁異常程式必須能夠區分由程式設計引起的異常以及由引用屬於程序地址空間但還尚未分配物理頁框的頁所引起的異常。在x86-ia32體系上由do_page_fault函式處理,每個版本有所差異,現分析的版本為2.6.32 /* regs:該結構包含當異常發生時的微處

linux核心分析--中斷中斷處理程式

寫在前面:       在前面的這篇文章講解了關於中斷的基本概念,從巨集觀的角度瞭解了中斷的基本知識。從這篇文章中要明白幾點知識       1、中斷是由硬體產生的非同步中斷,而異常則是處理器產生的同步中斷       2、中斷質上是一種特殊的電訊號,由硬體裝置發向處理器,

Linux核心——cli()和sti()——標誌暫存器的中斷標誌

cli()和sti()有點類似於彙編指令中的CLI和STL,當某個任務在執行的過程中不想被中斷,則可以在任務的開始出執行cli(),在任務的結束處執行sti(),恢復中斷的執行。 為了避免競爭條件和中斷對臨界程式碼區的干擾,在Linux 0.12核心程式碼中許多地方使用