c6x Linux 内核中断分析
?
?
1. 內核中斷介紹
?
1.1 中斷簡介
?
所有支持Linux的平臺都采用了中斷(interrupt)的概念,以便(因種種原因)引入周期性的中斷。需要區分兩種類型的中斷。
?
1. 硬件中斷(hardware interrupt):由系統自身和與之連接的外設自動產生。它們用于支持更高效地實現設備驅動程序,也用于引起處理器自身對異常或錯誤的關注,這些是需要與內核代碼進行交互的。?
?
2. 軟中斷(SoftIRQ):用于有效實現內核中的延期操作。?
?
在Linux中用于處理中斷和系統調用相關部分的代碼中,匯編和C代碼交織在一起, 以解決C語言無法獨立處理的一些特殊問題。在中斷處理中涉及到許多C代碼和底層的硬件交互代碼。
?
在C6678處理器上,硬件中斷的最大數目通常是15,這個值可不怎么大,還有考慮到有些中斷編號已經永久性地分配給了標準的系統組件(鍵盤、定時器等),因而限制了可用于其他外部設備的中斷編號數目。
?
外設可共享同一個中斷號這個現象稱為中斷共享(interrupt sharing)。但必須硬件和內核同時支持才能使用共享中斷,因為必須要識別出中斷來源于哪個設備。
?
1.2 中斷處理
?
在CPU得知發生中斷后,它將進一步的處理委托給一個軟件例程,該例程可能會修復故障、提供專門的處理或將外部事件通知用戶進程。
?
由于每個中斷和異常都有唯一的編號,內核使用一個數組,數組項是指向處理程序函數的指針。相關的中斷號根據數組項在數組中的位置判斷。
?
▲圖1.1 中斷處理過程
?
因為需要C語言代碼和匯編語言代碼之間的交互,所以必須特別小心,才能正確設計在匯編語言層次和C語言層次上的數據交換。對應的代碼位于arch/arch/kernel/entry.S中,徹底利用了各個處理器的具體特性。
?
1.3 中斷處理程序
?
中斷處理程序可能會遇到困難,特別是在處理程序執行期間,發生了其他中斷。盡管可以通過在處理程序執行期間禁用中斷來防止,但這會引起其他問題,如遺漏重要的中斷。屏蔽(Masking, 這個術語用于表示選擇性地禁用一個或多個中斷)因而只能短時間使用。
?
中斷處理數據結構:IRQ相關信息管理的關鍵點是一個全局數組,每個數組項對應一個IRQ編號。因為數組位置和中斷號是相同的,很容易定位與特定的IRQ相關的數組項:IRQ 0在位置0,IRQ 15在位置15,等等。IRQ最終映射到哪個處理器中斷。irq_desc存儲了中斷相關信息。
?
action鏈表提供了一個操作鏈,需要在中斷發生時執行。由中斷通知的設備驅動程序,可以將與之相關的處理程序函數放置在此處。有一個專門的數據結構用于表示這些操作。
?
chip硬件處理和芯片相關操作被封裝在chip中。為此引入了一個專門的數據結構用來處理硬件相關操作。
?
1.4 處理程序函數的表示
?
irqaction結構定義如下,每個處理程序函數都對應該結構的一個實例:
?
<code>
?
該結構中最重要的成員是處理程序函數本身,即handler成員,這是一個函數指針,位于結構的起始處。在設備請求一個系統中斷,而中斷控制器通過引發中斷將該請求轉發到處理器的時候,內核將調用該處理程序函數。
?
在考慮如何注冊處理程序函數時,我們再仔細考察其參數的語義。但請注意,處理程序的類型為irq_handler_t,與電流處理程序的類型irq_flow_handler_t顯然是不同的。
?
name和dev_id唯一地標識一個中斷處理程序。name是一個短字符串,用于標識設備(例如, 「e100」、「ncr53c8xx」等等),而dev_id是一個指針,指向在所有內核數據結構中唯一標識了該設備的數據結構實例,例如網卡的net_device實例。如果幾個設備共享一個IRQ,那么IRQ編號自身不能標識該設備,此時,在刪除處理程序函數時,將需要上述信息。
?
▲圖1.2 中斷結構體關系描述圖
?
2. 中斷向量表以及中斷子程序
?
2.1 中斷子程序實現
?
在匯編源文件arch/c6x/kernel/vectors.S 中定義了中斷子程序,使用宏IRQVEC可以實現不同的中斷子程序:
arch/c6x/kernel/vectors.S第?30 行實現了中斷處理宏定義IRQVEC
?
▲圖2.1 中斷處理向量宏定義
?
根據如上IRQVEC宏定義結合實際代碼使用情況可以生成不同的中斷子程序。
?
▲圖2.2 生成中斷向量
?
例如:
79行?IRQVEC INT4,_int4_handler可生成如下中斷子程序。
?
<code>
?
當中斷發生時,進入到匯編代碼中的中斷子程序,首先保存寄存器A0的值保存在棧中,然后拷貝相應的中斷子程序入口地址到A0寄存器中,然后跳轉到相應的中斷子程序,中斷子程序執行完畢后從棧中恢復寄存器A0的值。
?
在中斷子程序_int4_handler中執行如下指令
?
<code>
?
SAVE_ALL_INT此行作用為保存所有的寄存器到棧中,然后調用MASK_SYSCALL關閉系統調用,使用CALL_INT 4先將中斷號存入A4,然后調用kernel?中的?c6x_do_IRQ 中斷處理函數。調用結束后將返回結果存入寄存器B3。
?
▲圖2.3 CALL_INT宏定義
?
在arch/c6x/kernel/entry.S 中定義了諸多?_DEFINE_MACRO 匯編宏定義。
1. 220行?_DEFINE_MACRO(SAVE_ALL_INT)??中斷保存所有寄存器。
2. 325行?_DEFINE_MACRO(MASK_SYSCALL) 屏蔽系統調用。
?
2.1 c6x_do_IRQ處理流程
?
c6x_do_IRQ函數的是實現在arch/c6x/kernel/irq.c文件中
64行?asmlinkage void c6x_do_IRQ(unsigned int prio, struct pt_regs *regs)
?
▲圖2.4 c6x_do_IRQ中斷函數
?
獲取kernel中斷號:
?
c6x_do_IRQ 獲取中斷號prio,將中斷號通過hw_to_kernel_irq將硬件中斷號轉換為kernel中斷號,轉換后傳入kernel的中斷處理函數generic_handle_irq。
?
在代碼中hw_to_kernel_irq如何將硬件中斷號轉換為kernel中斷號?
?
在中斷初始化的過程中構建prio_to_irq的中斷對應數組,將硬件中斷號和kernel中斷號產生對應關系。當使用hw_to_kernel_irq的時候,hw_to_kernel_irq從中斷對應數組?prio_to_irq[(hw)] 中取出kernel中斷號。
?
常規內核處理函數generic_handle_irq?處理中斷流程:
?
在include/linux/irq.h 頭文件中實現了generic_handle_irq和
generic_handle_irq_desc兩個內聯函數。
?
generic_handle_irq 使用kernel中斷號irq獲取irq中斷號相對應的irq_to_desc,既irq_desc結構體數組,然后調用generic_handle_irq_desc將中斷號,和獲取的irq_desc結構體數組傳給generic_handle_irq_desc。
?
▲圖2.5 generic_handle_irq_desc
?
在generic_handle_irq_desc函數中檢測irq對應的irq_desc結構體中是否有中斷處理函數handle_irq。
?
如果有handle_irq就執行此中斷的中斷處理函數,這里執行的是handle_level_irq函數,在irq_desc->handle_irq中我們無法直接看到其對應的中斷處理函數,可以通過init_IRQ?初始化函數中獲得此處填入的中斷處理函數名稱。
?
▲圖2.6 init_IRQ 中斷初始化函數
?
handle_level_irq函數中執行的流程如下:
1. lock中斷。
2. 檢測中斷是否為處理中狀態:IRQ_INPROGRESS。
3.?如果中斷為正在處理的狀態直接跳到結尾處unlock中斷并推出函數。
4.?如果中斷為觸發狀態,將中斷設置為正在處理的狀態,并且調用handle_IRQ_event執行相應中斷處理函數。
?
如果沒有handle_irq就說明此中斷為共享中斷(IRQ線)執行__do_IRQ(irq)。
__do_IRQ 處理所有普通設備的中斷,每一個設備都有其特定的中斷處理函數,存在放action列表中。
?
在kernel/irq/handler.c文件中實現了__do_IRQ函數。__do_IRQ函數主要遍歷irq_desc 結構體中的action鏈表處理中斷。
?
449行?unsigned int __do_IRQ(unsigned int irq)
接下來使用共享中斷處理函數:handle_IRQ_event?來遍歷irq_desc 里的?action共享中斷鏈表。
?
handle_IRQ_event函數:
?
在kernel/irq/handle.c?368行irqreturn_t handle_IRQ_event( unsigned int irq,struct irqaction *action)Handle_IRQ_event?用來處理irq實際的action鏈表。
?
?
總結
以上是生活随笔為你收集整理的c6x Linux 内核中断分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js文件中怎么使用thymeleaf标签
- 下一篇: java foward_java 中se