成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

嵌入式實時系統的基石:ARM+Linux中斷設計實戰

系統 Linux
如果遇到未知的中斷源,函數會返回IRQ_NONE,表示該中斷未被處理,就像指揮官遇到無法識別的情況時,選擇暫時不采取行動,等待進一步的信息。

在當今數字化時代,高效計算是推動科技發展的核心動力。而 ARM 與 Linux 中斷系統,宛如開啟這扇高效計算大門的關鍵鑰匙。ARM 架構,以其低功耗、高性能的特性,廣泛應用于從智能手機到工業控制等各類設備,為系統運行奠定了堅實基礎。Linux 操作系統,則憑借開源、靈活的優勢,成為眾多開發者的首選。

那么,中斷系統在其中扮演著怎樣的角色呢?簡單來說,中斷系統如同一個智能管家,當外部設備(如鍵盤、鼠標)或內部事件(如定時器溢出)有服務請求時,它能迅速通知 CPU 暫停當前任務,轉而去處理緊急事務,處理完畢后又巧妙地讓 CPU 回到原來的工作中。這種機制大大提升了系統的響應速度與運行效率。特別是在 ARM 與 Linux 的組合中,中斷系統經過深度優化,進一步挖掘出兩者的潛力。在接下來的內容中,我們將深入剖析 ARM+Linux 中斷系統的工作原理、技術細節,一同探尋它是如何為高效計算賦能的。

一、中斷系統:計算世界的 “緊急響應者”

在日常生活中,我們難免會遭遇各種緊急狀況。當火災發生時,煙霧報警器會瞬間感應到異常,隨即發出尖銳的警報聲。這一警報就如同給消防系統發送了 “緊急信號”,消防員們收到信號后,會即刻停下手中的其他工作,迅速登上消防車,風馳電掣般趕赴火災現場。到達后,他們會爭分奪秒地展開滅火行動,直到成功撲滅大火,才會返回消防站,繼續待命,等待下一次任務。又比如在繁忙的交通道路上,突然發生了嚴重的交通事故,傷者急需救治。

這時,路人會馬上撥打 120 急救電話,急救中心接到電話后,會立即調度救護車,醫護人員放下手頭正在處理的事務,迅速前往事故地點,對傷者進行緊急救治,隨后將傷者送往醫院,完成任務后再回到崗位,準備應對下一次的急救需求。

在計算機的世界里,也存在著類似的 “緊急響應機制”,那就是中斷系統 。計算機的 CPU 就如同消防員或醫護人員,它平時有條不紊地執行著各種程序任務。而當外部設備(如鍵盤、鼠標、網卡等)或者內部事件(如定時器溢出)有緊急事情需要 CPU 處理時,就會向 CPU 發送中斷信號。CPU 在接收到中斷信號后,會暫時停下當前正在執行的任務,轉而去處理這個中斷請求。等處理完中斷后,再回到原來被中斷的任務處,繼續執行后續的指令。

ARM+Linux 中斷系統,正是基于 ARM 架構的硬件平臺與 Linux 操作系統相結合的中斷處理體系。ARM 架構憑借其低功耗、高性能以及豐富的外設接口等優勢,在嵌入式領域應用廣泛,從手機、平板電腦到工業控制設備、智能家居系統,到處都有它的身影。Linux 操作系統則以開源、穩定、可定制性強等特點,深受開發者的喜愛,在服務器領域占據著重要地位,同時也在嵌入式系統開發中被大量采用。ARM+Linux 中斷系統的高效運行,對于保障這些設備和系統的穩定運行、實時響應至關重要,是理解和開發基于 ARM+Linux 平臺應用的關鍵所在。

二、ARM 中斷的硬件基石

2.1ARM 的運行模式與異常分類

ARM 處理器擁有七種運行模式,就像是一位多才多藝的演員,可以根據不同的場景切換不同的 “角色” ,每種模式都有其獨特的用途和權限,共同協作維持著系統的穩定運行。

  • 用戶模式(User Mode):這是用戶程序正常執行的模式,就像普通市民在自己的生活空間內活動,權限相對有限。在這個模式下,程序只能執行自己的數據處理任務,不能隨意操作其他硬件資源,也無法直接切換到其他模式。例如,我們在手機上運行的各類 APP,它們大多運行在用戶模式下,受到系統的限制,不能隨意訪問底層硬件,以保障系統的安全和穩定。
  • 系統模式(System Mode):屬于特權模式,類似于城市管理者,雖然和用戶模式共用一套寄存器,但卻擁有更高的權限,可以不受用戶模式的限制,自由訪問系統資源。操作系統的一些特權任務會利用這個模式來訪問受控資源,比如對系統關鍵文件的讀寫操作等。
  • 中斷模式(IRQ Mode):用于處理一般的中斷請求,當硬件產生中斷信號后,就會自動進入該模式,如同城市中的普通應急響應部門,隨時準備處理各種常規的緊急情況。比如鍵盤按鍵按下、鼠標移動等操作產生的中斷,都會由中斷模式來處理。
  • 快速中斷模式(FIQ Mode):主要用于處理對時間要求緊急的中斷請求,就像是城市中的特種應急部隊,在高速數據傳輸及通道處理等場景中發揮關鍵作用。例如,在音視頻數據的實時傳輸過程中,為了保證數據的流暢性和及時性,相關的中斷就會由快速中斷模式來處理。它比普通中斷模式速度更快,這是因為它擁有更多的獨立寄存器(r8 - r14 以及 SPSR 都有對應的 banked 寄存器),模式切換時 CPU 自動保存和恢復這些寄存器的值,無需中斷處理程序手動操作,節省了時間 。而且它的入口地址(0x1c)與普通中斷(0x18)不同,由于跳轉范圍限制,至少少了一條跳轉指令,也提高了處理速度。
  • 管理模式(Supervisor Mode):這是 CPU 上電后的默認模式,是操作系統的保護模式,軟中斷(SWI)處理函數就在此模式下執行,就像城市的最高管理決策層。當用戶模式下的程序請求使用硬件資源時,會通過軟件中斷進入該模式。比如,用戶程序需要讀取磁盤文件時,就會觸發軟中斷,進入管理模式,由操作系統來完成具體的磁盤讀取操作。系統復位或開機時也會進入該模式,通常在此模式下進行系統的初始化工作。
  • 數據訪問終止模式(Abort Mode):當程序訪問非法地址或沒有權限讀取的內存地址時,就會進入該模式,類似于城市中的安全檢查部門在發現非法訪問時介入。在 Linux 下編程時經常出現的 segment fault 錯誤,通常就是在這個模式下拋出返回的。例如,程序試圖訪問一個未分配給它的內存區域,就會觸發數據訪問終止異常,進入該模式。
  • 未定義指令終止模式(Undefined Mode):當 CPU 在指令的譯碼階段不能識別該指令操作時,會進入此模式,用于支持硬件協處理器的軟件仿真,就像是城市中對未知情況的探索研究部門。比如,在不支持特定浮點運算指令的硬件上執行該指令時,就會進入未定義模式

在這七種模式中,除了用戶模式外,其他六種模式都屬于特權模式,擁有更高的權限,可以訪問系統的所有資源,并且能夠自由切換處理器模式。而特權模式中,除了系統模式外,其余五種模式(中斷模式、快速中斷模式、管理模式、數據訪問終止模式、未定義指令終止模式)又被統稱為異常模式,它們不僅可以通過特權程序切換進入,還能由特定的異常情況觸發進入。

中斷模式是ARM異常模式之一(IRQ模式,FIQ模式),是一種異步事件,如外部按鍵產生中斷,內部定時器產生中斷,通信數據口數據收發產生中斷等。

當一個異常產生時,以FIQ為例,CPU切入FIQ模式時

  • ①將原來執行程序的下一條指令地址保存到LR中,就是將R14保存到R14_fiq里面。
  • ②拷貝CPSR到SPSR_fiq。
  • ③改變CPSR模式位的值,改到FIQ模式。
  • ④改變PC值,將其指向相應的異常處理向量表。

離開異常處理的時候:

  • ①將LR(R14_fiq)賦給PC。
  • ②將SPSR(SPSR_fiq)拷貝到CPSR。
  • ③清除中斷禁止標志(如果開始時置位了)。

當一個外部IRQ中斷產生時:

  • ①處理器切換到IRQ模式
  • ②PC跳到0x18處運行,因為這是IRQ的中斷入口。
  • ③通過0x18:LDR PC, IRQ_ADDR,跳轉到相應的中斷服務程序。這個中斷服務程序就要確定中斷源,每個中斷源會有自己獨立的中斷服務程序。
  • ④得到中斷源,然后執行相應中斷服務程序
  • ⑤清除中斷標志,返回

這就是一個外部中斷完整的執行流程了,下面以具體寄存器來更具體的了解ARM的中斷機制。

假設ARM核有兩個中斷引腳,一根是irq pin,一根是fiq pin,正常情況下,ARM核只是機械地隨著PC指示去執行,當CPSR中的I位和F位都為1時,IRQ和FIQ都處于禁止狀態,這時候無論發什么信號,ARM都不會理睬。

當I位或F位為0時,irq pin有中斷信號過來時,ARM當前工作就會被打斷,切換到IRQ模式,并且跳轉到異常向量表的中斷入口0x18,SRCPND中相應位置1,經過檢查中斷優先級寄存器以及屏蔽寄存器,確定中斷源,INTPND相應位置1(經過仲裁,只有一位置1),這過程由ARM自動完成。0x18存放的是總的中斷處理函數,在這個函數里,可以建立一個二級中斷向量表,先清除SRCPND相應位,然后根據中斷源執行相應中斷服務程序,清除INTPND,返回。

及時清除中斷 Pending 寄存器的標志位是為了避免兩個問題:①發生中斷返回后,立即又被中斷,不斷的重復響應②丟失中斷處理過程中發生的中斷,返回后不響應。

在某個IRQ中斷程序執行過程中,有另外一個外部IRQ中斷產生,會將SRCPND相應位置1,等該中斷服務執行完,經過仲裁決定下一個要響應的中斷。但是假如當產生的是FIQ,則保存當前IRQ的現場,嵌套響應FIQ,FIQ服務程序執行完,再繼續執行IRQ服務。那么當一個FIQ正在服務,產生另外一個FIQ,會怎樣呢,答案是不會被打斷,跟IRQ一樣等當前中斷服務完成,再仲裁剩余需要相應的中斷。

所以得出這樣的結論:

①關于中斷嵌套:IRQ模式只能被FIQ模式打斷,FIQ模式下誰也打不斷。

②關于優先級:ARM核對中斷優先級,有明確的可編程管理。

下面再來看看Linux對ARM是怎么處理的,記住一個前提:Linux對ARM的硬件特性可以取舍,但不可更改。

建立異常向量表:系統從arch/arm/kernel/head.S的ENTRY(stext)開始執行,__lookup_processor_type檢查處理器ID,__lookup_machine_type檢查機器ID,__create_page_tables創建頁表,啟動MMU,然后由arch/arm/kernel/head_common.S 跳到start_kernel()->trap_init()

void __init trap_init(void)
{
    unsigned long vectors = CONFIG_VECTORS_BASE;
    …
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
    …
}
#define CONFIG_VECTORS_BASE 0xffff0000

CONFIG_VECTORS_BASE在autoconf.h定義,在ARM V4及V4T以后的大部分處理器中,中斷向量表的位置可以有兩個位置:一個是0,另一個是0xffff0000。可以通過CP15協處理器c1寄存器中V位(bit[13])控制。V和中斷向量表的對應關系如下:

V=0 ~ 0x00000000~0x0000001C
V=1 ~ 0xffff0000~0xffff001C

__vectors_end 至 __vectors_start之間為異常向量表,位于arch/arm/kernel/entry-armv.S

.globl __vectors_start
__vectors_start:
swi SYS_ERROR0
b vector_und + stubs_offset//復位異常
ldr pc, .LCvswi + stubs_offset //未定義異常
b vector_pabt + stubs_offset//軟件中斷異常
b vector_dabt + stubs_offset//數據異常
b vector_addrexcptn + stubs_offset//保留
b vector_irq + stubs_offset //普通中斷異常
b vector_fiq + stubs_offset//快速中斷異常
.globl __vectors_end
__vectors_end:

stubs_offset值如下:

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start

stubs_offset是如何確定的呢?

當匯編器看到B指令后會把要跳轉的標簽轉化為相對于當前PC的偏移量(±32M)寫入指令碼。從上面的代碼可以看到中斷向量表和stubs都發生了代碼搬移,所以如果中斷向量表中仍然寫成b vector_irq,那么實際執行的時候就無法跳轉到搬移后的vector_irq處,因為指令碼里寫的是原來的偏移量,所以需要把指令碼中的偏移量寫成搬移后的。我們把搬移前的中斷向量表中的irq入口地址記irq_PC,它在中斷向量表的偏移量就是irq_PC-vectors_start, vector_irq在stubs中的偏移量是vector_irq-stubs_start,這兩個偏移量在搬移前后是不變的。

搬移后 vectors_start在0xffff0000處,而stubs_start在0xffff0200處,所以搬移后的vector_irq相對于中斷向量中的中斷入口地址的偏移量就是,200+vector_irq在stubs中的偏移量再減去中斷入口在向量表中的偏移量,即200+ vector_irq-stubs_start-irq_PC+vectors_start = (vector_irq-irq_PC) + vectors_start+200-stubs_start,對于括號內的值實際上就是中斷向量表中寫的vector_irq,減去irq_PC是由匯編器完成的,而后面的 vectors_start+200-stubs_start就應該是stubs_offset,實際上在entry-armv.S中也是這樣定義的。

2.2ARM 中斷引腳與中斷使能控制

在 ARM 架構中,中斷的硬件實現依賴于特定的引腳和控制機制。我們可以假設 ARM 核心有兩根中斷引腳(實際中是集成在芯片內部,從外部不可見),一根叫 irq pin,用于普通中斷請求;另一根叫 fiq pin,用于快速中斷請求 。這兩根引腳就像是連接外部設備與 CPU 的 “緊急聯絡線”,一旦外部設備有緊急事務需要 CPU 處理,就會通過這兩根引腳向 CPU 發送中斷信號。

而 CPU 是否響應這些中斷信號,由當前程序狀態寄存器(CPSR)中的 I 位和 F 位來控制。當 CPSR 中的 I 位為 1 時,IRQ 中斷被禁止,此時即使 irq pin 上有中斷信號,CPU 也不會理會,就如同消防員將火警鈴聲設置為靜音,對火災警報充耳不聞;當 F 位為 1 時,FIQ 中斷被禁止,fiq pin 上的中斷信號也無法引起 CPU 的注意 。只有當 I 位和 F 位都為 0 時,irq pin 和 fiq pin 上的中斷信號才能成功打斷 CPU 當前正在執行的任務,使 CPU 切換到相應的中斷模式進行處理。例如,在系統進行一些關鍵的、不能被中斷干擾的操作時,就可以通過設置 CPSR 中的 I 位和 F 位來禁止中斷,確保操作的完整性和正確性。

當有外部中斷產生時,跳轉到異常向量表的“b vector_irq + stubs_offset //普通中斷異常”,進入異常處理函數,跳轉的入口位置 arch\arm\kernel\entry-armv.S 代碼簡略如下:

.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
vector_stub dabt, ABT_MODE, 8
vector_stub pabt, ABT_MODE, 4
vector_stub und, UND_MODE
/*
* Undefined FIQs
*/
vector_fiq:
disable_fiq
subs pc, lr, #4
vector_addrexcptn:
b vector_addrexcptn

vector_stub是個函數調用宏,根據中斷前的工作模式決定進入__irq_usr,__irq_svc。這里入__irq_svc,同時看到這里FIQ產生時,系統未做任何處理,直接返回,即Linux沒有提供對FIQ的支持,繼續跟進代碼

__irq_svc:
svc_entry
…
irq_handler

svc_entry是一個宏,主要實現了將SVC模式下的寄存器、中斷返回地址保存到堆棧中。然后進入最核心的中斷響應函數irq_handler,irq_handler實現過程arch\arm\kernel\entry-armv.S

.macro irq_handler
get_irqnr_preamble r5, lr
1: get_irqnr_and_base r0, r6, r5, lr @判斷中斷號,通過R0返回,3.5節有實現過程
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, 1b
bne asm_do_IRQ @進入中斷處理。
……
.endm

get_irqnr_and_base中斷號判斷過程,include/asm/arch-s3c2410/entry-macro.s

.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
mov \base, #S3C24XX_VA_IRQ
@@ try the interrupt offset register, since it is there
ldr \irqstat, [ \base, #INTPND ]
teq \irqstat, #0
beq 1002f
ldr \irqnr, [ \base, #INTOFFSET ] @通過判斷INTOFFSET寄存器得到中斷位置
…
@@ we have the value
1001:
adds \irqnr, \irqnr, #IRQ_EINT0 @加上中斷號的基準數值,得到最終的中斷號,注意:此時沒有考慮子中斷的具體情況。IRQ_EINT0在include/asm/arch-s3c2410/irqs.h中定義.從這里可以看出,中斷號的具體值是有平臺相關的代碼決定的,和硬件中斷掛起寄存器中的中斷號是不等的。
1002:
@@ exit here, Z flag unset if IRQ
.endm

asm_do_IRQ實現過程,arch/arm/kernel/irq.c

asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    struct irq_desc *desc = irq_desc + irq;//根據中斷號,找到響應的irq_desc

    /*
     * Some hardware gives randomly wrong interrupts. Rather
     * than crashing, do something sensible.
     */
    if (irq >= NR_IRQS)
        desc = &bad_irq_desc;

    irq_enter();

    desc_handle_irq(irq, desc);//根據irq和desc進入中斷處理

    /* AT91 specific workaround */
    irq_finish(irq);

    irq_exit();
    set_irq_regs(old_regs);
}
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);//中斷處理 
}

上述asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)使用了asmlinkage標識。那么這個標識的含義如何理解呢?

該符號定義在kernel/include/linux/linkage.h中,如下所示:

include//各個具體處理器在此文件中定義asmlinkage
#ifdef __cplusplus
        #define CPP_ASMLINKAGE extern "C"
        #else
        #define CPP_ASMLINKAGE
        #endif
#ifndef asmlinkage//如果以前沒有定義asmlinkage
        #define asmlinkage CPP_ASMLINKAGE
        #endif

對于ARM處理器的,沒有定義asmlinkage,所以沒有意義(不要以為參數是從堆棧傳遞的,對于ARM平臺來說還是符合ATPCS過程調用標準,通過寄存器傳遞的)。

但對于X86處理器的中是這樣定義的:

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

表示函數的參數傳遞是通過堆棧完成的。

中斷處理過程代碼就跟到這了,那么最后一個問題desc->handle_irq(irq, desc);是怎么跟我們注冊的中斷函數相關聯的呢?再從中斷模型注冊入手:

中斷相關的數據結構:在include/asm/arch/irq.h中定義。

irq_desc[]是一個指向irq_desc_t結構的數組, irq_desc_t結構是各個設備中斷服務例程的描述符。Irq_desc_t結構體中的成員action指向該中斷號對應的irqaction結構體鏈表。Irqaction結構體定義在include/linux/interrupt.h中,如下:

truct irqaction {
irq_handler_t handler; //中斷處理函數,注冊時提供
unsigned long flags; //中斷標志,注冊時提供
cpumask_t mask; //中斷掩碼
const char *name; //中斷名稱
void *dev_id; //設備id,本文后面部分介紹中斷共享時會詳細說明這個參數的作用
struct irqaction *next; //如果有中斷共享,則繼續執行,
int irq; //中斷號,注冊時提供
struct proc_dir_entry *dir; //指向IRQn相關的/proc/irq/n目錄的描述符
};

在注冊中斷號為irq的中斷服務程序時,系統會根據注冊參數封裝相應的irqaction結構體。并把中斷號為irq的irqaction結構體寫入irq_desc [irq]->action。這樣就把設備的中斷請求號與該設備的中斷服務例程irqaction聯系在一起了。當CPU接收到中斷請求后,就可以根據中斷號通過irq_desc []找到該設備的中斷服務程序。

2.3中斷向量表與中斷執行流程

中斷向量表在 ARM 中斷系統中扮演著至關重要的角色,它是中斷處理的 “導航地圖”。在 32 位 ARM 系統中,中斷向量表一般位于內存的特定地址,有兩個可選位置:一個是低地址 0x00000000 - 0x0000001c(Low vector) ;另一個是高地址 0Xffff0000 - 0xffff001c(High vector) ,具體使用哪個位置由 CPU 決定。中斷向量表中存放著各個中斷和異常對應的處理程序的入口地址,當有中斷或異常發生時,CPU 會自動將程序計數器(PC)指向中斷向量表中的相應地址,從而跳轉到對應的中斷服務例程進行處理。

以外部 IRQ 中斷為例,當 irq pin 上有中斷信號到來時,整個中斷執行流程如下:

  1. 模式切換:ARM 核心檢測到 irq pin 上的中斷信號后,首先會檢查 CPSR 中的 I 位,如果 I 位為 0(即 IRQ 中斷未被禁止),CPU 會自動切換到 IRQ 模式,就像消防員接到火災警報后,迅速換上消防服,進入戰斗狀態。
  2. 跳轉至中斷向量表:CPU 切換到 IRQ 模式后,PC 會跳到中斷向量表中 IRQ 對應的入口地址 0x18 處運行 。這個過程是硬件自動完成的,就像是消防系統根據警報類型,自動將消防員引導到對應的火災現場位置。
  3. 跳轉到中斷服務程序:在 0x18 地址處,通常存放著一條跳轉指令(如 LDR PC, IRQ_ADDR),通過這條指令,CPU 會跳轉到具體的中斷服務程序。這個中斷服務程序的任務是確定中斷源,因為可能有多個外部設備共享 irq pin,每個設備產生的中斷都需要有獨立的處理方式。比如,鍵盤和鼠標都可能通過 irq pin 發送中斷信號,中斷服務程序需要判斷到底是鍵盤按鍵按下產生的中斷,還是鼠標移動產生的中斷,然后再執行相應的處理操作。
  4. 執行中斷服務程序:確定中斷源后,CPU 會執行相應的中斷服務程序,完成對中斷事件的處理。例如,如果是鍵盤中斷,中斷服務程序可能會讀取鍵盤輸入的字符,并將其傳遞給相應的應用程序;如果是定時器中斷,可能會更新系統時間、觸發定時任務等。
  5. 清除中斷標志與返回:中斷服務程序執行完畢后,需要清除中斷標志,通知系統中斷已經處理完畢。然后,CPU 會恢復到中斷前的狀態,將程序執行流程返回到被中斷的地方繼續執行,就像消防員撲滅火災后,脫下消防服,回到原來的工作崗位,繼續之前未完成的任務。在這個過程中,需要將之前保存的寄存器值恢復,將 CPSR 從 SPSR 中恢復,將 LR 中的值賦給 PC 等,確保程序能夠無縫銜接繼續執行。

三、ARM+Linux 中斷系統基礎解析

3.1ARM 架構中斷機制

在 ARM 架構的璀璨星空中,中斷機制宛如一條貫穿其中的璀璨銀河,連接著處理器與外部設備,為系統的高效運行提供了關鍵支撐。ARM 架構下的中斷,猶如靈動的使者,根據其特性和應用場景,主要分為兩類:IRQ(Interrupt Request,外部中斷請求)和 FIQ(Fast Interrupt Request,快速中斷請求) ,它們各具特色,在不同的場景中發揮著獨特的作用。

IRQ,作為通用中斷請求的代表,是 ARM 架構中較為常見的中斷類型。當外部設備如鍵盤、鼠標、網絡接口等有事件需要處理器關注時,就會發出 IRQ 中斷請求。它就像是一位勤勞的信使,在系統中頻繁穿梭,將外部設備的各種請求傳遞給處理器。在我們日常使用的智能手機中,當用戶點擊屏幕時,觸摸屏控制器會產生 IRQ 中斷,通知處理器進行相應的處理,如打開應用程序、滑動頁面等。IRQ 中斷的特點是通用性強,能夠靈活地響應各種外部設備的請求,就像一個萬能的接口,連接著各種各樣的外部設備。而且,它還可以處理多個中斷請求的并發情況,當多個外部設備同時發出中斷請求時,IRQ 能夠有條不紊地將這些請求傳遞給處理器,由處理器按照一定的優先級順序進行處理,從而保證系統的實時性和可靠性 。

而 FIQ,則是中斷世界中的 “閃電俠”,以其快速響應的特點而著稱。它主要用于處理那些對時間要求極為苛刻、需要快速響應的中斷請求,如高速數據傳輸、通道處理等場景。在高速網絡通信中,數據的接收和發送需要及時處理,否則可能會導致數據丟失或延遲。此時,FIQ 就發揮了重要作用,它能夠在極短的時間內響應中斷請求,迅速處理數據,確保網絡通信的順暢。FIQ 之所以能夠如此快速地響應,得益于它擁有更高的優先級。

當 FIQ 和 IRQ 同時產生時,處理器會毫不猶豫地優先響應 FIQ 中斷,就像在緊急情況下,人們會優先處理最緊急的事務一樣。此外,FIQ 模式還提供了更多的 banked 寄存器,這些寄存器就像是為 FIQ 量身定制的 “快速通道”,在模式切換時,CPU 能夠自動保存和恢復相關寄存器的值,大大減少了上下文切換的開銷,從而提高了中斷處理的速度 。

在實際應用中,我們需要根據不同的場景需求,合理地選擇使用 IRQ 和 FIQ。對于那些對實時性要求不高的普通外部設備,如打印機、攝像頭等,使用 IRQ 中斷即可滿足需求;而對于那些對時間要求極高的高速數據傳輸設備,如高速 USB 接口、以太網控制器等,則需要使用 FIQ 中斷來確保系統的性能。

3.2Linux 對 ARM 中斷的管理

Linux 內核,就像一位智慧的指揮官,對 ARM 中斷進行著有條不紊的管理,確保系統的穩定運行。在這個過程中,中斷向量表的建立和中斷注冊是兩個關鍵的環節,它們就像是 Linux 內核管理 ARM 中斷的兩把 “利刃”,發揮著至關重要的作用。

中斷向量表,是中斷處理的 “導航地圖”,它記錄了每個中斷對應的處理程序的入口地址。在 ARM 架構中,中斷向量表的位置可以通過 CP15 協處理器的控制寄存器 C1 的 bit13 來設置,當該位為 0 時,中斷向量表起始于 0x00000000;當該位為 1 時,起始于 0xffff0000 。在 Linux 內核啟動的過程中,會精心地建立中斷向量表。它首先會確定中斷向量表的基地址,然后將異常向量表從內存的特定位置復制到中斷向量表的基地址處。異常向量表中包含了各種中斷和異常的處理入口,如復位異常、未定義指令異常、中斷異常等。

在 ARMv8 架構中,中斷向量表的建立過程涉及到多個步驟,包括初始化中斷控制器、設置中斷向量表的基地址、將異常向量表復制到中斷向量表的基地址等。通過這些步驟,Linux 內核為中斷處理搭建了一個堅實的基礎,使得處理器在接收到中斷請求時,能夠迅速找到對應的處理程序入口,就像在地圖上找到目的地的路線一樣。

中斷注冊,則是將設備的中斷處理函數與中斷號進行綁定的過程,就像是為每個中斷請求找到對應的 “處理專家”。在 Linux 內核中,設備驅動程序開發者需要使用 request_irq 函數來注冊中斷。這個函數的原型如下:

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id);

其中,irq 是要申請的硬件中斷號,它就像是設備的 “專屬號碼”,用于唯一標識一個中斷請求;handler 是向系統注冊的中斷處理函數,當該中斷發生時,系統會調用這個函數來處理中斷,它是處理中斷的核心代碼;irqflags 是中斷處理的屬性,如設置了 IRQF_DISABLED,則表示中斷處理程序是快速處理程序,在被調用時會屏蔽所有中斷,以確保快速處理中斷;如設置了 IRQF_SHARED,則表示多個設備可以共享這個中斷,這在一些資源有限的系統中非常有用;devname 是設置的中斷名稱,方便在系統中識別和管理中斷,就像給每個設備取一個名字一樣;dev_id 在中斷共享時會用到,一般設置為這個設備的設備結構體或者 NULL,它用于在共享中斷時區分不同的設備 。

當設備驅動程序調用 request_irq 函數成功注冊中斷后,Linux 內核會將中斷處理函數與中斷號關聯起來,并記錄在相應的中斷管理數據結構中。當中斷發生時,內核會根據中斷號找到對應的中斷處理函數,并調用它來處理中斷。在一個包含多個 GPIO 設備的嵌入式系統中,每個 GPIO 設備都可能產生中斷。設備驅動程序會為每個 GPIO 設備的中斷號注冊相應的中斷處理函數,當某個 GPIO 設備產生中斷時,內核能夠準確地找到對應的中斷處理函數,對中斷進行處理,從而實現設備與系統之間的高效通信 。通過中斷注冊,Linux 內核實現了對中斷的精細管理,使得系統能夠靈活地響應各種設備的中斷請求。

四、Linux在ARM上的中斷構建

4.1異常向量表的建立與設置

在 Linux 系統基于 ARM 架構啟動的過程中,異常向量表的建立是一個關鍵步驟,就像是搭建一座城市的應急指揮中心,為后續的中斷處理奠定基礎 。

ARM 架構支持將異常向量表放置在兩個不同的內存位置,具體由 CP15 協處理器 C1 寄存器的 bit [13](即 V 位)來控制 。當 V = 0 時,異常向量表位于低地址 0x00000000 - 0x0000001c;當 V = 1 時,異常向量表位于高地址 0Xffff0000 - 0xffff001c 。在大多數 ARM V4 及 V4T 以后的處理器中,通常會選擇使用高地址的異常向量表。

在 Linux 內核啟動時,start_kernel () 函數是整個內核初始化的起點,其中 trap_init ()(在一些內核版本中可能是 early_trap_init ())函數負責異常向量表的初始化工作 。以 early_trap_init () 函數為例,其內部實現過程如下:

void __init early_trap_init(void)
{
    unsigned long vectors = CONFIG_VECTORS_BASE;
    extern char __stubs_start[], __stubs_end[];
    extern char __vectors_start[], __vectors_end[];
    extern char __kuser_helper_start[], __kuser_helper_end[];
    int kuser_sz = __kuser_helper_end - __kuser_helper_start;

    /* 將異常向量表、stubs和kuser helpers拷貝到向量頁 */
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

    /* 拷貝信號返回處理函數到向量頁 */
    memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, sizeof(sigreturn_codes));

    /* 刷新指令緩存,使這些更改對指令流可見 */
    flush_icache_range(vectors, vectors + PAGE_SIZE);

    /* 修改異常向量表占據頁面的訪問權限 */
    modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}

首先,獲取 CONFIG_VECTORS_BASE 的值,該值通常在各個平臺的配置文件中設定,如在 arch/arm/configs/s3c2410_defconfig 中,CONFIG_VECTORS_BASE = 0xffff0000 ,它指定了異常向量表的目標地址。然后,通過 memcpy 函數將定義在 arch/arm/kernel/entry-armv.S 中的異常向量表(__vectors_end 至__vectors_start 之間的內容)拷貝到目標地址 vectors 處 。

同時,將異常處理程序的 stub(__stubs_end 至__stubs_start 之間的內容)拷貝到 vectors + 0x200 的位置,將 kuser helpers 拷貝到 vectors + 0x1000 - kuser_sz 的位置 。接著,把信號返回處理函數拷貝到特定位置。之后,調用 flush_icache_range 函數刷新指令緩存,確保 CPU 能夠獲取到最新的代碼。最后,使用 modify_domain 函數修改異常向量表占據頁面的訪問權限,使得只有核心態可以訪問該頁,保障系統的安全性。

在 arch/arm/kernel/entry-armv.S 文件中,定義了異常向量表的具體內容:

.globl __vectors_start
__vectors_start:
    swi SYS_ERROR0
    b vector_und + stubs_offset   //復位異常
    ldr pc, .LCvswi + stubs_offset   //未定義指令異常
    b vector_pabt + stubs_offset   //軟件中斷異常
    b vector_dabt + stubs_offset  //數據中斷異常
    b vector_addrexcptn + stubs_offset
    b vector_irq + stubs_offset   //普通中斷異常
    b vector_fiq + stubs_offset  //快速中斷異常
.globl __vectors_end
__vectors_end:

這里,每個異常類型都對應著一個特定的跳轉指令,例如普通中斷(IRQ)對應的是 b vector_irq + stubs_offset 。其中,stubs_offset 的值通過.equ stubs_offset, __vectors_start + 0x200 - __stubs_start 定義,它的作用是確保在異常向量表和 stubs 發生代碼搬移后,跳轉指令能夠正確地跳轉到搬移后的異常處理程序位置 。當 ARM 處理器發生異常時,會根據異常類型跳轉到異常向量表中對應的位置,進而跳轉到相應的異常處理程序處執行。

4.2中斷處理函數的流程解析

當硬件中斷觸發后,整個中斷處理流程就像是一場有條不紊的接力賽,涉及多個關鍵函數和操作,從硬件層面逐步過渡到軟件層面進行處理。

假設一個外部設備(如按鍵)產生了中斷信號,該信號首先會被 ARM 芯片的中斷控制器接收 。以常見的 ARM 芯片中斷控制器為例,它會將中斷信號進行初步處理,檢查中斷屏蔽寄存器和中斷優先級寄存器等,確定是否要將該中斷信號傳遞給 CPU 。如果中斷未被屏蔽且具有足夠的優先級,中斷控制器會通過 irq pin 向 CPU 發送中斷請求信號。

CPU 在執行當前指令的過程中,會不斷檢查是否有中斷信號到來 。當檢測到 irq pin 上的中斷信號后,且當前程序狀態寄存器(CPSR)中的 I 位為 0(表示 IRQ 中斷未被禁止)時,CPU 會立即做出響應 。首先,CPU 會自動切換到 IRQ 模式,這是硬件自動完成的操作,就像是消防員迅速切換到戰斗模式。然后,程序計數器(PC)會跳轉到異常向量表中 IRQ 對應的入口地址 0x18 處運行 。在 0x18 地址處,存放著一條跳轉指令(例如 LDR PC, IRQ_ADDR),通過這條指令,CPU 會跳轉到具體的中斷處理程序。

在 Linux 內核中,中斷處理程序的入口是 vector_irq,它定義在 arch/arm/kernel/entry-armv.S 文件中 。vector_irq 首先會進行一些現場保護操作,保存當前寄存器的值,以便在中斷處理完成后能夠恢復到中斷前的狀態 。然后,通過一系列的判斷和跳轉,最終會調用到 irq_handler 這個宏 。

irq_handler 宏實際上是調用 handle_arch_irq 函數,而 handle_arch_irq 函數是在中斷控制器初始化時注冊的處理函數 。在不同的 ARM 平臺中,handle_arch_irq 函數可能會有所不同,例如在使用 GIC(Generic Interrupt Controller)中斷控制器的平臺中,handle_arch_irq 函數通常會指向 gic_handle_irq 函數 。

gic_handle_irq 函數是 GIC 中斷控制器的核心處理函數,它的主要工作流程如下:

  1. 讀取中斷確認寄存器:首先,讀取 GIC 的中斷確認寄存器(GIC_CPU_INTACK),這個操作實際上是向 GIC 確認 CPU 已經接收到中斷信號 。同時,該寄存器會返回一個唯一的中斷號,用于標識產生中斷的設備。
  2. 處理中斷:根據返回的中斷號,查找對應的中斷處理函數,并調用該函數進行中斷處理 。在這個過程中,可能會涉及到對中斷源的進一步判斷和處理,例如讀取設備的狀態寄存器,獲取中斷的具體原因等。
  3. 通知中斷結束:當中斷處理完成后,需要向 GIC 發送中斷結束信號 。這通過寫入 GIC 的中斷結束寄存器(GIC_CPU_EOI)來實現,告知 GIC 本次中斷已經處理完畢,GIC 可以再次接受這個中斷號的中斷請求。

在驅動層面,當設備驅動注冊時,會使用 request_irq 函數來注冊中斷處理函數 。例如:

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);

其中,irq 是中斷號,handler 是具體的中斷處理函數,flags 用于指定中斷的屬性(如中斷觸發方式、是否共享中斷等),name 是中斷的名稱,dev 是傳遞給中斷處理函數的上下文數據 。當硬件中斷發生并經過上述的一系列處理后,最終會調用到驅動中注冊的中斷處理函數 handler 。在中斷處理函數中,會根據設備的具體需求進行相應的處理,例如讀取設備的數據、更新設備的狀態等 。處理完成后,中斷處理函數會返回一個 irqreturn_t 類型的值,用于告知系統中斷處理的結果。

當中斷處理函數執行完畢后,會進行現場恢復操作,將之前保存的寄存器值恢復,將 CPSR 從 SPSR 中恢復,將 LR 中的值賦給 PC 等,使 CPU 回到中斷前的狀態,繼續執行被中斷的任務 。

五、ARM的GIC中斷控制器剖析

5.1GIC 的基本概念與版本介紹

在 ARM 架構的中斷體系中,GIC(Generic Interrupt Controller)即通用中斷控制器,扮演著至關重要的角色,堪稱中斷管理的 “總指揮” 。它就像是城市中的交通指揮中心,負責收集、管理和分配來自各個外部設備以及內部事件的中斷信號,確保 CPU 能夠有條不紊地處理這些中斷請求。

目前,GIC 共有四個版本,分別為 V1 至 V4 。其中,V1 是最早期的版本,如今已逐漸被淘汰,不再廣泛應用;V2 版本應用較為廣泛,它最多支持 8 個 ARM core,常用于 ARMv7 - A 架構的芯片,如 Cortex - A7、Cortex - A9、Cortex - A15 等 ,像一些早期的智能手機、平板電腦等設備中,就常常能看到基于 GIC V2 的中斷控制;V3 和 V4 版本則主要面向 ARM64 服務器系統結構,支持更多的 ARM core,能夠滿足大規模數據處理和多任務并發的需求 ,在數據中心、云計算等領域發揮著重要作用。

例如,GIC - 500 最多支持 128 個 cpu core,它要求 ARM core 必須是 ARMV8 指令集的(如 Cortex - A57),符合 GIC architecture specification version 3 。在實際應用中,不同的芯片廠商會根據自身產品的需求,選擇合適版本的 GIC。例如,一些中低端的嵌入式設備可能會采用 GIC V2 版本,以降低成本并滿足基本的中斷處理需求;而高端的服務器芯片則會選用 GIC V3 或 V4 版本,以提供強大的中斷管理能力和高性能的計算支持。

5.2GIC 的中斷輸入信號類型

GIC 接收的中斷輸入信號主要分為三種類型,每種類型都有其獨特的特點和應用場景,就像是不同類型的緊急事件,需要不同的應對方式。

  1. PPI(私有外設中斷,Private Peripheral Interrupt):這是每個 CPU 私有的中斷,就如同每個人專屬的緊急聯絡方式。PPI 最多支持 16 個中斷,硬件中斷號從 ID16~ID31 。它通常會被發送到指定的 CPU 上,應用場景之一是 CPU 本地時鐘,比如每個核的 tick 中斷,用于進程調度使用 。以一個多核處理器為例,每個核心都有自己獨立的 PPI 中斷,用于處理與該核心緊密相關的特定外設事件,確保各個核心能夠高效地處理自身的事務,互不干擾。
  2. SPI(公用外設中斷,Shared Peripheral Interrupt):SPI 是所有 Core 共享的中斷,來自于外設,類似于公共的緊急求助通道。它最多可以支持 988 個外設中斷,硬件中斷號從 ID32~ID1019 。像我們常見的按鍵中斷、串口中斷等等,這些中斷所有的 Core 都可以處理,不限定特定 Core 。在一個智能家居系統中,多個傳感器(如溫度傳感器、濕度傳感器、門窗傳感器等)產生的中斷信號,都可以通過 SPI 中斷發送給 CPU,由 CPU 統一進行處理,實現對家居環境的智能監控和控制。

5.3GIC 的工作機制與寄存器功能

GIC 的工作機制主要依賴于兩個關鍵部分:仲裁單元(Distributor)和 CPU 接口模塊(CPU Interface) ,它們相互協作,共同完成中斷的管理和處理,就像是一個高效的團隊,各自發揮著重要的職責。

仲裁單元(Distributor)

仲裁單元就像是交通指揮中心的調度員,負責收集所有的中斷源,控制每個中斷的優先級,并將中斷事件分發到相應的 CPU 接口端 。它的主要工作包括:

  • 全局中斷使能控制:通過 GIC_DIST_CTRL 寄存器,可以對全局中斷進行控制 。一旦禁用了全局中斷,那么任何中斷源產生的中斷事件都不會被傳遞到 CPU 接口,就像是交通指揮中心關閉了所有的緊急通道,不再接收任何緊急信號。
  • 單個中斷使能或關閉控制:利用 GIC_DIST_ENABLE_CLEAR 寄存器,可以針對各個中斷源進行控制 。禁用某一個中斷源會導致該中斷事件不會被分發到 CPU 接口,但不影響其他中斷源產生的中斷事件的分發,就如同關閉了某個特定區域的緊急通道,但其他區域的緊急通道仍然暢通。
  • 中斷優先級設置:仲裁單元會為每個中斷分配優先級,它總是將優先級最高的中斷事件發送到 CPU 接口端 。例如,在一個同時有按鍵中斷和串口接收中斷的系統中,如果按鍵中斷被設置為更高的優先級,那么當兩個中斷同時發生時,仲裁單元會優先將按鍵中斷發送給 CPU 接口,確保關鍵事件能夠得到及時處理。
  • 中斷目標處理器列表設置:可以設置每個中斷應該發送到哪些 CPU 核心上,實現中斷的合理分配 。在一個多核處理器系統中,對于一些計算密集型的中斷任務,可以將其分配到性能較強的核心上進行處理,以提高系統的整體性能。
  • 中斷觸發模式設置:可以設定每個外部中斷的觸發模式,是電平觸發還是邊沿觸發 。例如,對于一些對時間精度要求較高的中斷事件,可以設置為邊沿觸發模式,確保能夠及時捕捉到中斷信號。
  • 中斷分組設置:設置每個中斷屬于組 0 還是組 1 。在安全系統中,Group0 通常用于安全相關的中斷,連接 FIQ;Group1 作為非安全系統使用,連接 IRQ 。通過這種分組方式,可以增強系統的安全性和穩定性。

CPU 接口模塊(CPU Interface)

CPU 接口模塊則像是連接交通指揮中心和消防員(CPU)的通信員,是分發器和 CPU Core 之間的橋梁 。它的主要功能如下:

  • 中斷請求信號使能或關閉:可以控制是否將中斷請求信號發送到 CPU Core 。如果禁用了中斷,那么即使仲裁單元分發了中斷事件到 CPU 接口,也不會向 CPU 發送中斷信號,就像是通信員關閉了與消防員的通信通道,消防員無法收到緊急任務通知。
  • 中斷應答:當 CPU 接收到中斷信號并開始處理時,會向 CPU 接口模塊應答中斷 。中斷一旦被應答,仲裁單元就會把該中斷的狀態從 pending 狀態修改成 active 。如果沒有后續 pending 的中斷,那么 CPU 接口就會取消中斷信號的發送;如果在這個過程中又產生了新的中斷,仲裁單元會把新中斷的狀態從 pending 狀態修改成 pending and active ,CPU 接口仍然會保持中斷信號的發送,通知 CPU 有新的中斷到來,就像是消防員向通信員回復已收到任務,并開始執行,通信員根據任務情況決定是否繼續發送新的任務通知。
  • 中斷處理完成通知:當中斷處理程序完成對一個中斷的處理后,會向 CPU 接口模塊的寄存器寫入信息,通知 GIC CPU 已經處理完該中斷 。這樣做一方面是通知仲裁單元將中斷狀態修改為 deactive,另一方面,可以允許其他 pending 的中斷向 CPU 接口提交,就像是消防員完成任務后向通信員報告任務完成,通信員收到報告后,允許新的緊急任務進入處理流程。
  • 優先級掩碼設置:通過設置優先級掩碼,可以屏蔽掉一些優先級比較低的中斷,這些中斷不會被通知到 CPU 。例如,在系統進行一些關鍵操作時,可以通過設置優先級掩碼,暫時屏蔽掉一些不重要的中斷,確保關鍵操作的順利進行,就像是在執行重要任務時,暫時忽略一些次要的緊急事件。
  • 搶占策略定義:定義中斷的搶占策略,當多個中斷到來的時候,選擇優先級最高的中斷通知給 CPU Core 。比如,當一個高優先級的中斷到來時,即使當前 CPU 正在處理一個低優先級的中斷,也會立即暫停當前處理,轉而處理高優先級的中斷,確保重要事件能夠得到優先處理,就像是在多個緊急任務同時出現時,優先安排消防員處理最緊急的任務。

在 GIC 的工作過程中,這些寄存器就像是交通指揮中心的各種控制按鈕和顯示屏,通過對它們的配置和操作,實現對中斷的精確管理和高效處理 。例如,在系統初始化階段,需要對 GIC 的寄存器進行正確的配置,包括中斷使能、優先級設置、觸發模式設置等,以確保系統能夠正常響應各種中斷請求 。在中斷處理過程中,CPU 通過讀取和寫入相關寄存器,與 GIC 進行交互,完成中斷的接收、處理和結束等操作 。

六、ARM+Linux中斷系統的應用與優化

6.1中斷處理程序的優化

中斷處理程序的優化,就像是對精密儀器的精細調校,每一個細節都關乎著系統的性能表現。從代碼結構和算法選擇等角度入手,能夠顯著提高中斷處理程序的響應速度,讓系統在面對各種中斷請求時更加游刃有余 。

在代碼結構方面,要遵循簡潔高效的原則。將中斷處理程序劃分為關鍵部分和非關鍵部分,是一種行之有效的策略。關鍵部分負責處理緊急且必須立即完成的任務,如讀取硬件寄存器狀態、清除中斷標志等,這些任務對時間要求極高,需要在最短的時間內完成 。而非關鍵部分則可以將一些相對耗時但并非緊急的任務,如數據處理、通知其他模塊等,放到中斷處理程序之外執行,避免在中斷處理過程中占用過多時間,影響系統對其他中斷的響應 。

在一個網絡通信設備的中斷處理程序中,當接收到數據包時,關鍵部分迅速讀取數據包的關鍵信息,如源地址、目的地址等,并清除中斷標志,以確保能夠及時響應下一個中斷請求;而非關鍵部分則將數據包的解析和處理工作放到一個專門的線程中執行,這樣既保證了中斷處理的及時性,又不影響數據包的后續處理。

算法選擇也是優化中斷處理程序的重要一環。選擇高效的算法能夠大大減少中斷處理的時間開銷,提高系統的性能。在數據處理任務中,不同的算法在時間復雜度和空間復雜度上存在差異,選擇合適的算法可以顯著提高處理效率 。假設需要對中斷處理過程中接收到的大量數據進行排序,冒泡排序算法的時間復雜度為 O (n^2),而快速排序算法的平均時間復雜度為 O (n log n) 。

在數據量較大的情況下,使用快速排序算法能夠比冒泡排序算法節省大量的時間,從而提高中斷處理程序的執行效率。因此,在編寫中斷處理程序時,要根據具體的任務需求,仔細分析和選擇最優的算法,以達到提高性能的目的 。

6.2硬件資源的合理配置

硬件資源的合理配置,是提升中斷系統整體性能的關鍵所在,就像是為一臺高性能電腦精心挑選和搭配各個硬件組件,使其發揮出最佳性能。在 ARM+Linux 中斷系統中,GIC(通用中斷控制器)作為核心硬件組件,對它的合理配置至關重要 。

GIC 的配置涉及多個方面,其中中斷優先級的設置是關鍵環節之一。中斷優先級決定了系統在處理多個中斷請求時的先后順序。在實際應用中,要根據不同中斷源的重要性和實時性要求,合理地分配中斷優先級 。對于那些對實時性要求極高的中斷源,如高速數據傳輸設備的中斷請求,應將其優先級設置為較高值,確保系統能夠在第一時間響應并處理這些中斷,避免數據丟失或延遲 。

而對于一些對實時性要求相對較低的中斷源,如打印機等設備的中斷請求,可以將其優先級設置為較低值。在一個工業自動化控制系統中,傳感器的數據采集中斷和電機控制中斷對實時性要求很高,將它們的優先級設置為較高,而系統日志記錄等輔助功能的中斷優先級則設置為較低,這樣可以保證系統在關鍵任務上的實時性,同時也不影響其他非關鍵任務的正常執行 。

中斷路由的配置也不容忽視。中斷路由決定了中斷請求如何被分發到相應的 CPU 核心進行處理。在多核處理器系統中,合理的中斷路由配置可以充分利用各個 CPU 核心的處理能力,提高系統的并行處理能力 。可以根據不同中斷源的負載情況和 CPU 核心的利用率,將中斷請求均勻地分配到各個 CPU 核心上,避免某個 CPU 核心負載過高,而其他核心閑置的情況 。

在一個多線程的網絡服務器應用中,將網絡接收中斷和網絡發送中斷分別路由到不同的 CPU 核心上處理,能夠充分發揮多核處理器的優勢,提高網絡數據的處理速度 。通過合理配置 GIC 的中斷優先級和中斷路由等硬件資源,可以有效地提升中斷系統的整體性能,使系統更加穩定、高效地運行 。

6.3代碼實現

中斷注冊與注銷代碼示例:

在 ARM+Linux 的世界里,中斷注冊與注銷是與中斷系統交互的基礎操作,就像在一場盛大的音樂會上,為每一位演奏者安排好出場順序和離場流程。讓我們通過一段具體的代碼示例:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/irq.h>

static irqreturn_t my_interrupt_handler(int irq, void *dev_id) {
    // 中斷處理邏輯
    printk(KERN_INFO "My Interrupt Handler: IRQ %d\n", irq);
    return IRQ_HANDLED;
}

static int __init my_module_init(void) {
    int irq_num = 50; // 假設使用的中斷號為50,實際應用中需根據硬件確定
    int ret;

    // 中斷注冊
    ret = request_irq(irq_num, my_interrupt_handler, IRQF_TRIGGER_RISING, "my_device", NULL);
    if (ret) {
        printk(KERN_ERR "Failed to request IRQ %d: %d\n", irq_num, ret);
        return ret;
    }

    printk(KERN_INFO "Successfully registered IRQ %d\n", irq_num);
    return 0;
}

static void __exit my_module_exit(void) {
    int irq_num = 50;

    // 中斷注銷
    free_irq(irq_num, NULL);
    printk(KERN_INFO "Successfully unregistered IRQ %d\n", irq_num);
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("ARM+Linux Interrupt Example");

在這段代碼中,request_irq函數是中斷注冊的核心。它的第一個參數irq_num是要申請的硬件中斷號,就像是為設備分配的一個獨特的 “身份證號碼”,在實際應用中,這個號碼需要根據硬件的實際連接和配置來確定。第二個參數my_interrupt_handler是指向中斷處理函數的指針,當中斷發生時,系統就會調用這個函數來處理中斷,它就像是一位專業的 “問題解決者”,負責處理中斷帶來的各種任務。

第三個參數IRQF_TRIGGER_RISING是中斷處理的屬性,這里表示該中斷是上升沿觸發,就像運動員聽到發令槍響(上升沿)后開始起跑一樣,系統在檢測到中斷信號的上升沿時,會觸發中斷處理程序。第四個參數"my_device"是與中斷相關聯的設備名稱,方便在系統中識別和管理中斷,就像給每個設備貼上一個標簽。最后一個參數NULL是設備的私有數據字段,在中斷共享時會用到,一般設置為這個設備的設備結構體或者NULL 。

free_irq函數則用于中斷注銷,它的第一個參數是要釋放的中斷號,第二個參數與注冊時的dev_id相對應,用于確保正確地釋放中斷資源。在中斷注銷時,就像是讓演奏者有序地離開舞臺,系統會清理與該中斷相關的資源,確保系統的穩定運行 。

中斷處理函數編寫:

中斷處理函數是中斷系統的核心,它就像是一位經驗豐富的指揮官,在中斷發生時,迅速做出決策,指揮系統應對各種情況。讓我們來看一個完整的中斷處理函數代碼示例,并深入分析其中的處理邏輯。

static irqreturn_t my_interrupt_handler(int irq, void *dev_id) {
    // 檢查中斷源
    if (irq == IRQ_BUTTON) {
        // 處理按鍵中斷
        printk(KERN_INFO "Button Interrupt Detected\n");
        // 讀取按鍵狀態
        int button_state = read_button_state();
        if (button_state == PRESSED) {
            // 執行按鍵按下的操作
            perform_button_press_action();
        } else {
            // 執行按鍵松開的操作
            perform_button_release_action();
        }
    } else if (irq == IRQ_TIMER) {
        // 處理定時器中斷
        printk(KERN_INFO "Timer Interrupt Detected\n");
        // 更新定時器相關的變量
        update_timer_variables();
        // 執行定時器到期的任務
        perform_timer_expiry_task();
    } else {
        // 未知中斷源,返回未處理
        return IRQ_NONE;
    }

    // 表示中斷已處理
    return IRQ_HANDLED;
}

在這個中斷處理函數中,首先通過if - else if語句來檢查中斷源。這就像是指揮官在戰場上,首先要明確敵人的來源,才能做出正確的戰略部署。如果中斷源是按鍵(IRQ_BUTTON),則讀取按鍵狀態,并根據狀態執行相應的操作,就像根據敵人的行動做出不同的應對策略。如果是定時器中斷(IRQ_TIMER),則更新定時器相關的變量,并執行定時器到期的任務。

如果遇到未知的中斷源,函數會返回IRQ_NONE,表示該中斷未被處理,就像指揮官遇到無法識別的情況時,選擇暫時不采取行動,等待進一步的信息。而當所有的中斷處理完成后,函數返回IRQ_HANDLED,表示中斷已被成功處理,就像指揮官成功完成了一次戰斗任務,向總部匯報勝利的消息 。通過這樣的處理邏輯,中斷處理函數能夠有條不紊地應對各種中斷情況,確保系統的穩定和高效運行。

責任編輯:武曉燕 來源: 深度Linux
相關推薦

2022-01-03 23:33:40

Linux組件系統

2009-05-27 19:28:20

Linux磁場設計

2009-07-17 16:06:59

ARM嵌入式開發

2010-01-07 10:45:38

嵌入式Linux入門

2020-07-03 07:00:00

Linux組件

2021-12-19 22:34:45

Linux容器系統

2011-06-17 08:59:27

QT Embedded Arm

2010-01-12 17:32:40

ARM平臺

2012-07-30 14:13:11

Linux 2.6內核嵌入式

2009-06-26 16:05:04

嵌入式Linux

2011-01-14 13:13:23

嵌入式Linux開發

2022-12-14 08:06:08

2023-01-04 09:37:16

2017-12-21 10:43:44

Linux嵌入式終端

2011-04-25 10:25:43

OpenEmbedde嵌入式Linux

2011-01-06 15:11:09

嵌入式linux

2009-04-11 15:22:24

Linux 2.6內核應用

2020-06-15 07:00:00

Linux嵌入式系統

2009-04-11 15:12:24

2009-04-10 13:17:32

opensusesuselinux
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲成人毛片 | 国产精品无 | 播放一级毛片 | 欧美视频一级 | 中文字幕亚洲在线 | 久久男女视频 | 国产一级毛片精品完整视频版 | 国产精品久久国产愉拍 | 国产欧美日韩在线一区 | 中文字幕国产视频 | 国产91久久久久久久免费 | 91在线网站 | 中文字幕日韩欧美 | 成人网av | 国产乱码精品一区二区三区中文 | 爱爱无遮挡 | 亚洲欧美中文日韩在线v日本 | 亚洲国产一区二区视频 | 欧美不卡 | 中文字幕精品一区二区三区精品 | 黄色成人在线 | 国产一二三区电影 | 亚洲欧美日韩精品久久亚洲区 | 久久久久久综合 | 伊人久久大香线 | 亚洲高清在线观看 | 激情欧美日韩一区二区 | 九九热精品视频 | 国产sm主人调教女m视频 | 91影院在线观看 | 欧美精品久久一区 | 狠狠操狠狠搞 | 成人黄色av网站 | 91在线精品秘密一区二区 | 99re| 久久久精品黄色 | 欧美一级特黄aaa大片在线观看 | 久久久久免费精品国产 | 国产精品久久国产精品 | 亚洲精品一区二区冲田杏梨 | 黄色毛片免费看 |