歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

Linux 2.6中斷下半部機制分析

摘要 本文主要從使用者的角度對Linux 2.6內核的下半部機制softirq、tasklet和workqueue進行分析,對於這三種機制在內核中的具體實現並未進行深入分析,倘若讀者有興趣了解,可以直接閱讀Linux內核源代碼的相關部分。

說明 本文檔由流星自網上收集整理,按照自由軟件開放源代碼的精神發布,任何人可以免費獲得、使用和重新發布,但是你沒有限制別人重新發布你發布內容的權利。發布本文的目的是希望它能對讀者有用,但沒有任何擔保,甚至沒有適合特定目的的隱含的擔保。更詳細的情況請參閱GNU通用公共許可證(GPL),以及GNU自由文檔協議(GFDL)。


目 錄
1 概述
2 Linux 2.6內核中斷下半部機制
2.1 softirq機制
2.2 tasklet機制
2.3 workqueue機制
3 幾種下半部機制的比較
4 下半部機制的選擇
5 Linux與NGSA的下半部機制比較
5.1 NGSA中斷下半部機制分析
5.2 NGSA下半部機制缺陷分析


1 概述
中斷服務程序往往都需要在CPU關中斷的情況下運行,以避免中斷嵌套而使控制復雜化,但是關中斷的時間又不能太長,否則會造成中斷信號的丟失。為此,在Linux中,將中斷處理程序分為兩部分,即上半部和下半部。上半部通常用於執行跟硬件關系密切的關鍵程序,這部分執行時間非常短,而且是在關中斷的環境下運行的。對時間要求不是很嚴格,而且通常比較耗時的一些操作,則交給下半部來執行,這部分代碼是在開中斷中執行的。上半部處理硬件相關,稱為硬件中斷,這通常需要立即執行。下半部則可以延遲一定時間,在內核合適的時間段來執行程序,這就是我們這裡要討論的軟中斷。
本文以目前最新版本的Linux內核2.6.22為例,來討論Linux的中斷下半部機制。在2.6版本的內核中,下半部機制主要由softirq、tasklet和workqueue來實現,下面著重對這3種機制進行分析。


2 Linux 2.6內核中斷下半部機制
老版本的Linux內核中,下半部是以一種叫做Bottom Half(簡稱為BH)的機制來實現的,最初它是借助中斷向量來實現的,在系統中用一組(共32個)函數指針,分別表示32個中斷向量,這種實現方式目前在2.4版本的內核中還可以看到它的身影。但是目前在2.6版本的內核中已經看不到它了。現在的Linux內核,一般以一種稱為softirq的軟中斷機制來實現下半部。

2.1 softirq機制
原來的BH機制有兩個明顯的缺陷:一是系統中一次只能有一個CPU可以執行BH代碼,二是BH函數不允許嵌套。這在單處理器系統中或許沒關系,但在SMP系統中卻是致命的缺陷。但是軟中斷機制就不一樣了。Linux的softirq機制與SMP是緊密相連的,整個softirq機制的設計與實現始終貫穿著一個思想:“誰觸發,誰執行”(Who marks, who runs),也就是說,每個CPU都單獨負責它所觸發的軟中斷,互不干擾。這就有效地利用了SMP系統的性能和特點,極大地提高了處理效率。
Linux在include/linux/interrupt.h中定義了一個softirq_action結構來描述一個softirq請求,如下所示:
struct softirq_action
{
void (*action)(struct softirq_action *);
void *data;
};
其中,函數指針action指向軟中斷請求的服務函數,而data則指向由服務函數自行解釋的參數數據。
基於上述結構,系統在kernel/softirq.c中定義了一個全局的softirq軟中斷向量表softirq_vec[32],對應32個softirq_action結構表示的軟中斷描述符。但實際上,Linux並沒有使用到32個軟中斷向量,內核預定義了一些軟中斷向量的含義供我們使用:
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
#ifdef CONFIG_HIGH_RES_TIMERS
HRTIMER_SOFTIRQ,
#endif
};
其中HI_SOFTIRQ用於實現高優先級的軟中斷,比如高優先級的hi_tasklet,而TASKLET_SOFTIRQ則用於實現諸如tasklet這樣的一般性軟中斷。關於tasklet,我們在後面會進行介紹。我們不需要使用到32個軟中斷向量,事實上,內核預定義的軟中斷向量已經可以滿足我們絕大多數應用的需求。其他向量保留給今後內核擴展使用,我們不應去使用它們。
要使用softirq,我們必須先初始化它。我們使用open_softirq()函數來開啟一個指定的軟中斷向量nr,初始化nr對應的描述符softirq_vec[nr],設置所有CPU的軟中斷掩碼的相應位為1。函數do_softirq()負責執行數組softirq_vec[32]中設置的軟中斷服務函數。每個CPU都是通過執行這個函數來執行軟中斷服務的。由於同一個CPU上的軟中斷服務例程不允許嵌套,因此,do_softirq()函數一開始就檢查當前CPU是否已經正處在中斷服務中,如果是則立即返回。在同一個CPU上,do_softirq()是串行執行的。
使用open_softirq()注冊完一個軟中斷之後,我們需要觸發它。內核使用函數raise_softirq()來觸發一個軟中斷。對於一個指定的softirq來說,只會有一個處理函數,這個處理函數是所有CPU共享的。由於同一個softirq的處理函數可能在不同的CPU上同時執行,並產生競爭條件,處理函數本身的同步機制是非常重要的。激活一個軟中斷一般在中斷的上半部中執行。當一個中斷處理程序想要激活一個軟中斷時,raise_softirq()就會被調用。在後來的某個時刻,當do_softirq()在某個CPU上運行時,就會調用相關的軟中斷處理函數。
需要注意的是,在softirq機制中,還包含有一個很小的內核線程ksoftirqd。這是為了平衡系統負載而設的。試想,如果系統一直不斷觸發軟中斷請求,CPU就會不斷地去處理軟中斷,因為至少每次時鐘中斷都會執行一次do_softirq()。這樣一來,系統中其他重要任務不是要因長期得不到CPU而一直處於饑餓狀態嗎?在系統繁忙的時候,這個小小的內核線程就顯得特別有用了,過多的軟中斷請求會被放到系統合適的時間段執行,給其他進程更多的執行機會。
在2.6內核中,do_softirq()被放到irq_exit()中執行。在中斷上半部的處理中,只在irq_exit()中才調用do_softirq()進行軟中斷的處理,這非常有利於軟中斷模塊的升級和移植。如果需要在我們的NGSA中移植Linux的軟中斷,這樣的處理確實給了我們許多便利,因為我們只需要對我們的中斷上半部的執行作很小的改動。如果在中斷上半部有許多軟中斷調用的入口,那我們的移植豈不是會很痛苦?

Copyright © Linux教程網 All Rights Reserved