Posted on 2008-04-09 22:52
silentneil 閱讀(161)
評論(0) 編輯 收藏 引用
1、中斷處理程序的局限:
* 中斷處理程序以異步方式執(zhí)行并且它有可能會打斷其他重要代碼的執(zhí)行。因此,它們應(yīng)該執(zhí)行得越快越好。
* 如果當前有一個中斷處理程序正在執(zhí)行,在最好的情況下,與該中斷同級的其他中斷會被屏蔽,在最壞的情況下,所有其他中斷都會被屏蔽。因此,仍應(yīng)該讓它們執(zhí)行得越快越好。
* 由于中斷處理程序往往需要對硬件進行操作,所以它們通常有很高的時限要求。
* 中斷處理程序不在進程上下文中運行,所以它們不能阻塞。
2、下半部的任務(wù)就是執(zhí)行與中斷處理密切相關(guān)但中斷處理程序本身不執(zhí)行的工作。
3、并不存在嚴格明確的規(guī)定來說明到底什么任務(wù)應(yīng)該在哪個部分中完成,如何做決定完全取決于驅(qū)動程序開發(fā)者自己的判斷。對于在上半部和下半部之間劃分工作,盡管不存在某種嚴格的規(guī)則,但還是有一些提示可供借鑒:
* 如果一個任務(wù)對時間非常敏感,將其放在中斷處理程序中執(zhí)行。
* 如果一個任務(wù)和硬件相關(guān),將其放在中斷處理程序中執(zhí)行。
* 如果一個任務(wù)要保證不被其它中斷(特別是相同的中斷)打斷,將其放在中斷處理程序中執(zhí)行。
* 其他所有任務(wù),考慮放置在下半部執(zhí)行。
4、和上半部分只能通過中斷處理程序?qū)崿F(xiàn)不同,下半部可以通過多種機制實現(xiàn)。最早的Linux只提供"bottom half"這種機制用于實現(xiàn)下半部,這種機制也被稱為"BH"。不久,內(nèi)核開發(fā)者們就引入了任務(wù)隊列機制來實現(xiàn)工作的推后執(zhí)行,并用來代替BH機制。目前這兩種機制已經(jīng)在2.5之后的版本中被去除。在2.6版本中,內(nèi)核提供了三種不同形式的下半部實現(xiàn)機制:軟中斷、tasklet和工作隊列。其實還有另外一個可以用于將工作推后執(zhí)行的機制是內(nèi)核定時器。它可以把操作推遲到某個確定的時間段之后執(zhí)行。也就是說,當你必須保證在一個確定的時間段過去后再運行時,你應(yīng)該使用內(nèi)核定時器。
5、軟中斷是一組靜態(tài)定義的下半部接口,有32個,可以在所有處理器上同時執(zhí)行-即使兩個類型相同也可以。其適合于像網(wǎng)絡(luò)這樣對性能要求非常高的情況。此外,軟中斷必須在編譯期間就進行靜態(tài)注冊。
6、軟中斷的實現(xiàn):軟中斷的代碼位于kernel/softirq.c文件中。其由softirq_action結(jié)構(gòu)表示,定義在< linux/interrupt.h>中。在kernel/softirq.c中定義了一個包含有32個該結(jié)構(gòu)體的數(shù)組。目前這32個項中只用到6 個。一個軟中斷不會搶占另外一個軟中斷。實際上,唯一可以搶占軟中斷的是中斷處理程序。一個注冊的軟中斷必須在被標記后才會執(zhí)行。這被稱作觸發(fā)軟中斷。通常,中斷處理程序會在返回前標記它的軟中斷,使其在稍后被執(zhí)行。在下列地方,待處理的軟中斷會被檢查和執(zhí)行:
* 在處理完一個硬件中斷以后。
* 在ksoftirqd內(nèi)核線程中。
* 在那些顯式檢查和執(zhí)行待處理的軟中斷的代碼中,如網(wǎng)絡(luò)子系統(tǒng)中。
不管用什么辦法喚起,軟中斷都要在do_softirq()中執(zhí)行。該函數(shù)很簡單,如果有待處理的軟中斷,do_softirq()就會循環(huán)遍歷每一個,調(diào)用它們的處理程序。
7、使用軟中斷:軟中斷保留給系統(tǒng)中對時間要求最嚴格以及最重要的下半部使用。目前只有兩個子系統(tǒng)-網(wǎng)絡(luò)和SCSI直接使用軟中斷。此外,內(nèi)核定時器和tasklets都是建立在軟中斷上的。對于時間要求嚴格并能自己高效地完成加鎖工作的應(yīng)用,軟中斷會是正確的選擇。
1)分配索引
在編譯期間,通過<linux/interrupt.h>中定義的一個枚舉類型來靜態(tài)地聲明軟中斷。內(nèi)核用這些從0開始的索引來表示一種相對優(yōu)先級。索引號小的軟中斷在索引號大的軟中斷之前執(zhí)行。建立一個新的軟中斷必須在此枚舉類型中加入新的項。而加入時,不能像在其他地方一樣,簡單地把新項加到列表的末尾。相反,你必須根據(jù)你希望賦予它的優(yōu)先級來決定加入的位置。
2)注冊你的處理程序
接著,在運行時通過調(diào)用open_softirq()注冊軟中斷處理程序。軟中斷處理程序執(zhí)行的時候,允許響應(yīng)中斷,但它自己不能休眠。在一個處理程序運行的時候,當前處理器上的軟中斷被禁止。但其他處理器仍可以執(zhí)行別的軟中斷。實際上,如果一個軟中斷在它被執(zhí)行的同時再次被觸發(fā)了,那么另外一個處理器可以同時運行其處理程序。這意味著任何共享數(shù)據(jù)-甚至是僅在軟中斷處理程序內(nèi)部使用的全局變量都需要嚴格的鎖保護。如果僅僅通過互斥的加鎖方式來防止它自身的并發(fā)執(zhí)行,那么使用軟中斷就沒有任何意義。因此大部分軟中斷處理程序都通過采取單處理器數(shù)據(jù)或其他一些技巧來避免顯式地加鎖,從而提供更出色的性能。
3)觸發(fā)你的軟中斷
raise_softirq()函數(shù)可以將一個軟中斷設(shè)置為掛起狀態(tài),讓它在下次調(diào)用do_softirq()函數(shù)時投入運行。該函數(shù)在觸發(fā)一個軟中斷之前先要禁止中斷,觸發(fā)后再恢復(fù)回原來的狀態(tài)。如果中斷已經(jīng)被禁止了,那可以調(diào)用另一函數(shù)raise_softirq_irqoff(),這會帶來一些優(yōu)化效果。在中斷處理程序中觸發(fā)軟中斷是最常見的形式。在這種情況下,中斷處理程序執(zhí)行硬件設(shè)備的相關(guān)操作,然后觸發(fā)相應(yīng)的軟中斷,最后退出。內(nèi)核在執(zhí)行完中斷處理程序以后,馬上就會調(diào)用do_softirq()函數(shù)。于是軟中斷開始執(zhí)行中斷處理程序留給它去完成的剩余任務(wù)。
8、Tasklets是利用軟中斷實現(xiàn)的一種下半部機制。它和進程沒有任何關(guān)系。Tasklets和軟中斷在本質(zhì)上很相似,行為表現(xiàn)也相近,但是它的接口更簡單,鎖保護也要求較低。選擇到底是用軟中斷還是tasklets其實很簡單:通常你應(yīng)該用tasklets,軟中斷的使用者屈指可數(shù)。它只在那些執(zhí)行頻率很高和連續(xù)性要求很高的情況下才需要。
9、tasklet是通過軟中斷實現(xiàn)的,所以它們本身也是軟中斷。Tasklets由tasklet_struct結(jié)構(gòu)表示。每個結(jié)構(gòu)體單獨代表一個tasklet,其定義在<linux/interrupt.h>中。已調(diào)度的tasklet(等同于被觸發(fā)的軟中斷)存放在兩個單處理器數(shù)據(jù)結(jié)構(gòu):tasklet_vec(普通tasklet)和tasklet_hi_vec(高優(yōu)先級tasklet)中。這兩個數(shù)據(jù)結(jié)構(gòu)都是由 tasklet_struct結(jié)構(gòu)體構(gòu)成的鏈表。Tasklets由tasklet_schedule()和tasklet_hi_schedule() 函數(shù)進行調(diào)度。
10、使用Tasklets
1)聲明自己的Tasklet
既可以使用<linux/interrupt.h>中定義的兩個宏中的一個DECLARE_TASKLET或 DECLARE_TASKLET_DISABLED來靜態(tài)創(chuàng)建tasklet,前者把創(chuàng)建的tasklet的引用計數(shù)器設(shè)置為0,該tasklet處于激活狀態(tài)。另一個把引入計數(shù)器設(shè)為1,所以該tasklet處于禁止狀態(tài)。還可以使用tasklet_init()動態(tài)創(chuàng)建一個tasklet。
2)編寫自己的tasklet處理程序
tasklet處理程序必須符合規(guī)定的函數(shù)類型:void tasklet_handler(unsigned long data)。因為是靠軟中斷實現(xiàn),所以tasklet不能睡眠。這意味著你不能在tasklet中使用信號量或其他什么阻塞式函數(shù)。如果你的 tasklet和其他的tasklet或軟中斷共享了數(shù)據(jù),你必須進行適當?shù)逆i保護。
3)調(diào)度自己的tasklet
通過調(diào)用tasklet_schedule()函數(shù)來調(diào)度。在tasklet被調(diào)度以后在其還沒有得到運行機會之前,如果一個相同的tasklet又被調(diào)度了,那么它仍只會運行一次。而如果這時它已經(jīng)開始運行了,那么這個新的tasklet會被重新調(diào)度并再次運行。作為一種優(yōu)化措施,一個tasklet總在調(diào)度它的處理器上執(zhí)行-這是希望更好地利用處理器的高速緩存。可以調(diào)用tasklet_disable()函數(shù)來禁止某個指定的tasklet,也可以調(diào)用tasklet_enable()函數(shù)激活一個tasklet。還可以調(diào)用tasklet_kill()函數(shù)從掛起的隊列中去掉一個tasklet。
11、每個處理器都有一組輔助處理軟中斷的內(nèi)核線程。當內(nèi)核中出現(xiàn)大量軟中斷的時候,這些內(nèi)核進程就會輔助處理它們。這些內(nèi)核線程在最低的優(yōu)先級上運行(nice值是19),這能避免它們跟其他重要的任務(wù)搶奪資源,但它們最終肯定會被執(zhí)行。
12、工作隊列是另外一種將工作推后執(zhí)行的形式,它和我們之前討論過的所有其他形式都不相同。工作隊列可以把工作推后,交由一個內(nèi)核線程去執(zhí)行-該工作總是會在進程上下文執(zhí)行。如果你需要用一個可以重新調(diào)度的實體來執(zhí)行你的下半部處理,你應(yīng)該使用工作隊列。它是唯一能在進程上下文運行的下半部實現(xiàn)的機制,也只有它才可以睡眠。這意味著在你需要獲得大量的內(nèi)存時、在你需要獲取信號量時,在你需要執(zhí)行阻塞式的I/O操作時,它都會非常有用。
13、工作隊列子系統(tǒng)是一個用于創(chuàng)建內(nèi)核線程的接口,通過它創(chuàng)建的進程負責(zé)執(zhí)行由內(nèi)核其他部分排到隊列里的任務(wù)。它創(chuàng)建的這些內(nèi)核線程被稱作工作者線程。工作隊列子系統(tǒng)提供了一個缺省的工作者線程來處理需要推后的工作。不過如果需要在工作者線程中執(zhí)行大量的處理操作,也可以創(chuàng)建屬于自己的工作者線程。這么做有助于減輕缺省線程的負擔(dān),避免工作隊列中其他需要完成的工作處于饑餓狀態(tài)。
14、下半部機制的選擇:簡單地說,一般的驅(qū)動程序編寫者需要做兩個選擇。首先,你是不是需要一個可調(diào)度的實體來執(zhí)行需要推后完成的工作-你有休眠的需要嗎?要是有,工作隊列就是你的唯一選擇。否則最好用tasklet。要是必須專注于性能的提高,那么就考慮軟中斷吧。