一、Linux中中斷原理
1、中斷的分類
在Linux操作系統中,中斷的分類是非常複雜的。根據不同的角度,可以將中斷分爲不同的類型。
根據中斷的來源,中斷可分爲內部中斷和外部中斷,內部中斷的中斷源來自CPU內部(軟件中斷指令、溢出、除法錯誤等,例如,操作系統從用戶態切換到內核態需藉助CPU內部的軟件中斷) ,外部中斷的中斷源來自CPU外部,由外設提出請求。
根據是否可以屏蔽中斷分爲可屏蔽中斷與不屏蔽中斷 (NMI) ,可屏蔽中斷可以通過屏蔽字被屏蔽,屏蔽後,該中斷不再得到響應,而不屏蔽中斷不能被屏蔽。
根據中斷入口跳轉方法的不同,中斷分爲向量中斷和非向量中斷。採用向量中斷的 CPU 通常爲不同的中斷分配不同的中斷號,當檢測到某中斷號的中斷到來後,就自動跳轉到與該中斷號對應的地址執行。不同中斷號的中斷有不同的入口地址。非向量中斷的多箇中斷共享一個入口地址,進入該入口地址後再通過軟件判斷中斷標誌來識別具體是哪個中斷。也就是說,向量中斷由硬件提供中斷服務程序入口地址,非向量中斷由軟件提供中斷服務程序入口地址。
2、Linux中斷處理程序架構
設備的中斷會打斷內核中進程的正常調度和運行,系統對更高吞吐率的追求勢必要求中斷服務程序儘可能地短小精悍。爲了在中斷執行時間儘可能短和中斷處理需完成大量工作之間找到一個平衡點, Linux將中斷處理程序分解爲兩個半部:頂半部(top half)和底半部(bottom half) 。
頂半部完成儘可能少的比較緊急的功能,它往往只是簡單地讀取寄存器中的中斷狀態並清除中斷標誌後就進行“登記中斷”的工作。 “登記中斷”意味着將底半部處理程序掛到該設備的底半部執行隊列中去。這樣,頂半部執行的速度就會很快,可以服務更多的中斷請求。
這樣,中斷處理工作的重心就落在了底半部的頭上,它來完成中斷事件的絕大多數任務。底半部幾乎做了中斷處理程序所有的事情,而且可以被新的中斷打斷,這也是底半部和頂半部的最大不同,因爲頂半部往往被設計成不可中斷。底半部則相對來說並不是非常緊急的,而且相對比較耗時,不在硬件中斷服務程序中執行。
儘管頂半部、底半部的結合能夠改善系統的響應能力,但是,如果中斷要處理的工作本身很少,則完全可以直接在頂半部全部完成。
3、Linux中斷實現過程
3.1、中斷信號線(IRQ)
中斷信號線是對中斷輸入線和終端輸出線的統稱。終端輸入線是指接受中斷信號的引腳,中斷輸出線是指發送中斷信號的引腳。
3.2、中斷控制器
中斷控制器位於ARM處理器核心和中斷源之間,外部中斷源將中斷髮到中斷控制器,中斷控制器根據優先級進行判斷,然後通過引腳將中斷請求發送給ARM處理器核心。
3.3、申請和釋放中斷
在Linux設備驅動中,使用中斷的設備需要申請和釋放對應的中斷,分別使用內核提供的request_irq()和free_irq()函數。
1.申請IRQ
int request_irq(unsigned int irq,
void (*handler)(int irq, void *dev_id, struct pt_regs *regs),
unsigned long irqflags,
const char * devname,
void *dev_id);
在Linux2.6中,該函數有<kernel/irq/Manage.c>實現。
irq 是要申請的硬件中斷號。
handler 表示要註冊的中斷處理函數指針,是一個回調函數,中斷髮生時,系統調用這個函數來處理中斷,dev_id參數將被傳遞給它。
irqflags 是中斷處理的屬性,若設置了 SA_INTERRUPT,則表示中斷處理程序是快速處理程序,快速處理程序被調用時屏蔽所有中斷,慢速處理程序不屏蔽;若設置了SA_SHIRQ,則表示多個設備共享中斷。
devname 表示設備的名字,該名字會在/proc/interrupts中顯示。
dev_id 這個指針是爲共享中斷線而設立的。如果不需要中斷共享中斷線,那麼只要將該指針設爲 NULL即可。 很多資料中都建議將設備結構指針作爲dev_id參數
request_irq() 返回0表示成功,否則返回對應錯誤的負值,返回-INVAL 表示中斷號無效或處理函數指針爲NULL,返回-EBUSY 表示中斷已經被佔用且不能共享。
2.釋放IRQ
當設備不需要中斷線時,需要釋放中斷線,中斷信號線是非常緊缺的。
與request_irq()向對應的函數爲free_irq(),free_irq()的原型如下:
void free_irq(unsigned int irq,void *dev_id);
free_irq()中參數的定義與request_irq()相同。
3.4、底半部機制
Linux系統實現底半部的機制主要有tasklet、工作隊列和軟中斷(後續補充)
3.5、