中斷處理程式的侷限:
1、中斷處理程式以非同步的方式執行,並且它有可能會打斷其他重要**的執行。
2、如果當前有乙個中斷處理程式正在執行,最好的情況是,與該中斷同級的其他中斷會被遮蔽,最壞情況下,當預處理器上所有其他中斷都會被遮蔽。
3、中斷處理程式往往需要對硬體進行操作,所以他們不能阻塞。這限制了他們所作的事情。
8.1 下半部
8.1.2 下半部的環境
3、軟中斷和tasklet
軟中斷是一組靜態定義的下半部介面,有32個,可以在所有處理器上同時執行——及時兩個型別相同也可以。
tasklet:兩個不同型別的tasklet可以在不同的處理器上同時執行,但型別相同的tasklet不能同時執行。軟中斷必須在靜態編譯期間就進行註冊。如此相反,tasklet可以通過**動態註冊。
核心定時器把操作推遲到某個確定的時間段之後執行。
8.2 軟中斷
8.2.1 軟中斷的實現
軟中斷是在編譯期間靜態分配的。它不像tasklet那樣能被動態的註冊或登出。軟中斷由softirq_action結構表示。
乙個軟中斷不會搶占另外乙個軟中斷。唯一可以搶占軟中斷的是中斷處理程式。其他的軟中斷甚至是相同型別的軟中斷可以在其他處理器上同時執行。
在下列地方,待處理的軟中斷會被檢查和執行:
1、從乙個硬體中斷**處返回時
2、在ksoftirqd核心執行緒中
3、在那些顯示檢查和執行待處理的軟中斷**中,如網路子系統中
不管用什麼辦法喚起,軟中斷都要在do_softirq()中執行。
8.2.2 使用軟中斷
軟中斷保留給系統中對時間要求最嚴格以及最重要的下半部使用。目前,只有兩個子系統(網路和scsi)直接使用軟中斷。此外,核心定時器和tasklet都是簡歷在軟中斷上的。軟中斷處理程式執行時,允許響應中斷,但它自己不能休眠。在乙個處理程式執行的時候,當預處理器上的軟中斷被禁止。但其他處理器仍可以執行別的軟中斷。如果同乙個軟中斷在它被執行的同時再次觸發了,那麼另乙個處理器可以同時執行這個處理程式。這意味著任何共享資料都需要嚴格保護。因此,大部分軟中斷處理沖虛,都通過採取單處理器資料或其他一些技巧來避免枷鎖。
raise_softirq()函式可以講乙個軟中斷設定為掛起狀態,讓他在下次呼叫do_softirq()的時候投入執行。
在中斷處理程式中觸發軟中斷是最常見的形式。在這種情況下,中斷處理程式執行硬體裝置的相關操作,然後觸發相應的軟中斷,最後推出。核心在執行完中斷處理程式後會馬上執行do_softirq()函式。
8.3 tasklet
8.3.1
因為tasklet是通過軟中斷實現的,所以其本身也是軟中斷。tasklt有兩類軟中斷代表:hi_softirq和tasklet_softirq。hi_softirq優先執行。
8.3.2 使用tasklet
1、宣告tasklet
靜態:declare_tasklet(name,func,data);tasklet處於啟用狀態
delcare_tasklet_disable(name,func,data);tasklet禁止狀態
動態:struct tasklet_struct my_tasklet={null,0,atomic_init(0),my_tasklet_handler,dev)
tasklet_init(t,tasklet_handler,dev);
2、編寫tasklet處理程式
void tasklet_handler(unsigned long data)
因為是靠軟中斷實現,所以tasklet不能睡眠。不能使用阻塞式函式,由於tasklet執行時允許響應中斷,所以必須做好預防工作。
3、排程tasklet
tasklet_schedule(&my_tasklet);
在tasklet被排程後,只要有機會就會盡可能的執行。在它還沒有得到執行機會之前,如果有乙個相同的tasklet又被排程了,那麼它仍然只能執行一次。作為一種優化措施,乙個tasklet總在排程它的處理器上執行——更好的利用處理器快取。
tasklet_disable();
tasklet_disable_nosync();
tasklet_enable();
tasklet_kill();從掛起的佇列中去掉乙個tasklet。如果tasklet正在執行,該函式等待其執行完畢後,從佇列中去掉tasklet。
4、ksoftirqd
核心不會立即處理重新觸發的軟中斷。當大量軟中斷出現後,核心會喚醒一組核心執行緒來處理這些負載。這些執行緒在最低優先順序上執行(nice 19),這能避免與其他重要的任務搶奪資源。但他們最終肯定會執行,所以這個方案能夠保證在軟中斷負擔很重的時候,使用者程式不會因為得不到處理器時間而處於飢餓狀態。相應的,也能保證「過量」的軟中斷終究會得到處理。在空閒系統上,這個方案表現良好,軟中斷處理的非常迅速(因為核心執行緒會馬上排程)。
8.4 工作佇列
工作佇列可以把工作推後,交由乙個核心執行緒去執行——這個下半部分總是會在程序上下文中執行。工作佇列允許重新排程和睡眠。
8.4.1 工作佇列的實現
工作佇列子系統是乙個用於建立核心執行緒的介面,通過它建立的程序負責執行由其他核心其他部分排到佇列中的人物。它建立的這些核心執行緒稱作工作者執行緒。工作佇列子系統提供了乙個預設的工作者執行緒來處理這些工作。
8.4.2 使用工作佇列
1、建立推後的工作
declare_work(name,void(*func)(void*),void *data);
init_work(struct work_struct *work,void(*func)(void *),void *data);
2、工作佇列處理函式
void work_handler(void *data)
這個函式會由乙個工作者執行緒執行,因此,函式會執行在程序上下文中。預設情況下,允許響應中斷,並且不持有任何鎖。如果需要,函式可以睡眠。需要注意的是,儘管操作處理函式執行在程序上下文中,但它不能訪問使用者空間,因為核心執行緒在使用者空間沒有相關的記憶體對映。通常發生在系統呼叫時,核心會代表使用者空間的程序執行,此時它才能訪問使用者空間,也只有在此時它才會對映使用者空間的記憶體。
3、對工作進行排程
想要把給定的工作的處理函式提交給預設的events工作者執行緒:
schedule_work(&work);
schedule_delayed_work(&work);
4、重新整理操作
void flush_scheduled_work(void);
函式會一直等待,知道佇列中所有物件都被執行以後才返回。在等待所有待處理的工作執行的時候,該函式會進入休眠狀態,所以只能在程序上下文使用它。
int cancel_delayed_wrok(struct work_struct *work);
5、建立新的工作佇列
struct workqueue_struct *create_workqueue(const char *name);
int queue_work(struct workqueue_struct *wq,struct work_struct *work);
int queue_delayed_work(struct wrokqueue_struct *wq,struct work_struct *work,unsigned long delay);
flush_workqueue(struct workqueue_struct *wq);
8.5 下半部機制的選擇
8.6 在下半部之間加鎖
使用tasklet的好處在於,他自己負責執行的序列化保障:兩個相同的tasklet不允許同時執行,即使在不同的處理器上也不行。
8.7 禁止下半部
一般的單純禁止下半部是不夠的,為了保證共享資料的安全,更常見的做法是,先得到乙個鎖然後再禁止下半部的處理。驅動程式中通常使用的都是這種方法。
local_bh_disable();函式通過preempt_count(核心搶占的時候使用的也是它)為每乙個程序維護乙個計數器。當計數器變為0時,下半部才能夠被處理。
第8章 下半部和推後執行的工作
8.2 軟中斷 討論從實際的下半部實現 軟中斷方法開始。軟中斷使用得比較少 而tasklet是下半部更常用的一種形式。但是,由於tasklet是通過軟中斷實現的,先來研究軟中斷。軟中斷的 位於kernel softirq.c檔案中。8.2.1 軟中斷的實現 軟中斷是在編譯期間靜態分配的。它不像tas...
第8章 下半部和推後執行的工作
中斷處理程式作為上半部,執行時要麼禁用所有中斷,要麼禁用當前線上的中斷,要麼禁用當前處理機上的同級中斷來保證當前中斷處理的正常執行 下半部用於處理中斷服務的其它任務,允許推後執行,包括軟中斷,tasklet 基於軟中斷 工作佇列 靜態編譯,只有32個軟中斷,目前只用到了9個。靜態結構體陣列中都可含有...
linux中斷的上半部和下半部
本文 與linux中斷息息相關的乙個重要概念是linux中斷分為兩個半部 上半部 tophalf 和下半部 bottom half 上半部的功能是 登記中斷 當乙個中斷發生時,它進行相應地硬體讀寫後就把中斷例程的下半部掛到該裝置的下半部執行佇列中去。因此,上半部執行的速度就會很快,可以服務更多的中斷...