標籤:
中斷和時鐘技術可以提升驅動程式的效率
中斷
中斷在Linux中的實現
通常情況下,一個驅動程式只需要申請中斷,並添加中斷處理函數就可以了,中斷的到達和中斷函數的調用都是核心實現架構完成的。所以程式員只要保證申請了正確的中斷號及編寫了正確的中斷處理函數即可。
中斷的宏觀分類
1.硬中斷
由系統硬體產生的中斷。系統硬體通常引起外來事件。外來事件事件具有隨機性和突發性,因此硬體中斷也具有隨機性和突發性。
2.非強制中斷
非強制中斷是執行中斷指令時產生的。非強制中斷不用外設施加插斷要求訊號,因此非強制中斷的發生不是隨機的是程式安排好的。組譯工具中非強制中斷指令int n,n必須是中斷向量。
處理器接收非強制中斷有兩個來源:(1)處理器執行到錯誤的指令代碼;(2)軟體產生中斷,如進程的調度就是使用的非強制中斷。
中斷產生的位置分類
1.外部中斷
外部中斷一般是由電腦外設發出的插斷要求,如鍵盤中斷、定時器中斷等。外部中斷是可以通過編程方式給予屏蔽的。
2.內部中斷
內部中斷指因硬體出錯(如突然斷電、同位錯等)或運算出錯(除數為0、運算溢出、單步中斷等)所引起的中斷。內部中斷是非遮罩式插斷。通常情況下,大多數內部中斷由Linux核心進行了處理,所以驅動程式員不需要關心這些問題。
同步和非同步中斷
1.同步中斷
同步中斷是指令執行的過程中由CPU控制的,CPU在執行完一條指令後才發出中斷。也就是說,在指令執行的過程中,即時有中斷的到來,只要指令還沒執行完,CPU就不會去執行該中斷。同步中斷一般是因為程式錯誤所引起的,例如記憶體管理中的缺頁中斷,被0除出錯等。當CPU決定處理同步中斷時,會調用異常處理函數,使系統從錯誤的狀態恢複過來。當錯誤不可恢複時,就會死機會藍屏。
2.非同步中斷
非同步中斷是由硬體裝置隨機產生的,產生中斷時並不考慮與處理器時鐘同步問題,該類型的中斷時可以隨時產生。例如在網路卡驅動程式中,當網卡接收到資料包後,會向CPU發送一個非同步中斷事件,表示資料到來,CPU並不知道何時將接收該事件。非同步中斷的中斷處理函數與核心的執行順序是非同步執行的,兩者沒有必然的聯絡,也不會互相影響。
中斷的實現過程
中斷的實現過程較為複雜,涉及中斷訊號線、中斷控制器等概念。
1.中斷訊號線(IRQ)
中斷訊號線是對中斷輸入線和中斷輸出線的統稱。中斷輸入線是指接收中斷資訊的引腳。中斷輸出線是指發送中斷資訊的引腳。每一個能夠產生中斷的外設都有一條或者多條中斷輸出線(簡稱IRQ),用來通知處理器產生中斷。
2.中斷控制器
中斷控制器位於ARM處理器核心和中斷源之間。外部中斷源將中斷髮送到中斷控制器。中斷控制器根據優先順序進行判斷,然後通過引腳將插斷要求發送給ARM處理器核心。
當外部中斷同時產生中斷時,中斷優先順序產生邏輯會判斷哪一個中斷將被執行。中斷屏蔽寄存器:當 屏蔽位為0表示對應的中斷可以正常執行,否則對應的中斷被禁止。
中斷處理過程
中斷的安裝與釋放
當裝置需要中斷功能時,應該安裝中斷。如果驅動程式員沒有通過安裝中斷的方式通知Linux核心需要使用中斷,那麼核心只會簡單的應答並且忽略該中斷。
1.申請中斷線
申請中斷線可以使核心指導外設應該使用哪一個中斷號,哪一個中斷處理函數。
函數實現:kernel/irq/Manage.c
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev){return request_threaded_irq(irq, handler, NULL, flags, name, dev);}
2.釋放中斷線 kernel/irq/Manage.c
void free_irq(unsigned int irq, void *dev_id){kfree(__free_irq(irq, dev_id));}
按鍵中斷執行個體
時鐘機制
Linux驅動程式中經常會使用一些時鐘機制,主要是用來延時一段時間。在這段時間內硬體裝置可以完成相應的工作。
時間度量
一個與時鐘中斷相關的全域變數HZ。時鐘中斷是由系統定時硬體以周期性的間隔產生,這個周期性的值由HZ來表示。
HZ一般被定義為1000,表示一秒鐘時鐘中斷髮生1000次。每當時鐘中斷髮生時,核心內部計數器的值就會加上1。內部計數器由jiffies變數表示,當系統初始化時,這個變數被設定為0。
時間延時
C語言中,經常使用sleep()函數將程式延時一段時間,這個函數能夠實現毫秒級的延時。在裝置驅動程式中,很多對裝置的操作也需要延時一段時間,使裝置完成某些特定的任務。
Linux核心中,兩種常見的延時技術
1.短時延時
當裝置驅動程式需要等待硬體處理的完成時,會主動地延時一段時間。這個時間一般是幾十毫秒,甚至更短的時間。例如,驅動程式向裝置的某個寄存器寫入資料時,由於寄存器的寫入速度較慢,所以需要驅動程式等待一定的時間,然後繼續執行下面的工作。
static inline void ndelay(unsigned long x);static inline void udelay(unsigned long usecs);static inline void msleep(unsigned int msecs);
2.長時延時
長時延時實現一般是比較當前jiffies和目標jiffies的值。長時延時可以使用忙等待來實現。
延時3秒鐘的執行個體:
unsigned long timeout = jiffies + 3*HZ;while(time_before(jiffies, timeout));
time_before宏簡單的比較兩個時間值得大小。如果參數1<參數2,返回true。
To be continue...
Linux驅動設計—— 中斷與時鐘