上半部:接收到中斷就立即執行,只做有嚴格時限的工作,如對中斷應答或復位硬件。
下半部 : 能夠被允許稍後完成的工作推遲到下半部執行。
中斷上下文:沒有後備進程,不可睡眠。中斷處理程序打斷了其他的代碼。
Local_irq_disable();local_irq_enable();
Unsigned long flags; local_irq_save(flags);local_irq_restore(flags);
Disable_irq(int);禁止中斷向所有處理器的中斷。
Linux的上半部就是中斷處理程序,下半部有多種機制:
軟中斷是一組靜態定義的下半部接口,有32個,可以在所有處理器上同時執行,類型相同也可以;在編譯時靜態注冊。
struct softirq_action{ // 表示軟中斷
void (*action)(struct softirq_action*);
}
32個目前用了6個。
static struct softirq_action soft_irq_vec[NR_SOFTIRQS];//kernel/softirq.c軟中斷數組
中斷處理程序:內核運行軟中斷處理程序的時候,執行action函數。
一個軟中斷不會搶占另外一個軟中斷。唯一可以搶占軟中斷的是中斷處理程序。其他的軟中斷甚至同類型的可以在其他處理器上同時執行 執行軟件中斷:一個注冊的軟件中斷在標記後才會執行,這稱作觸發中斷。
中斷處理程序在返回前標記軟中斷。
在:硬件中斷代碼返回時;在ksoftirq內核線程中;顯示檢查執行軟中斷 處,待處理的軟中斷會被檢查和執行
軟中斷在do_softirq中執行
u32 pending;
pending = local_sofqirq_pending();
if(pending){
struct softirq_action* h;
set_softirq_pending(0);
h = softirq_vec;
do{
if(pending & 1){
h->action();
}
h++;
pending >>=1;
}while(pending);
}
使用軟中斷
軟中斷留給對時間要求最嚴格及最重要的下半部使用。目前只有網絡,scsi使用內核定時器和tasklet都建立在軟中斷上。
通過枚舉類型靜態聲明軟中斷,並分配索引 注冊處理程序
open_softirq(NET_TX_SOFTIRQ,net_tx_action);軟中斷處裡程序執行時,允許響應中斷,但不能睡眠。由於只禁止當前處理器上的運行,其他處理器可以同時運行處理程序,需要加鎖保護。 rase_softirq(NET_TX_SOFTIRQ)將軟中斷設置為掛起狀態,下次再調用do_softirq時執行。
Tasklet
基於軟件中斷實現的,靈活性強,動態創建的下半部實現機制。兩個不同類型的tasklet可以在不同處理器上運行,但相同的不可以。可以通過代碼動態注冊。
實現:基於軟中斷
struct tasklet_struct {
struct tasklet_struct *next;
unsigned long sate;//(0/TASKLET_STATE_SCHED/TASKLET_STATE_RUN)
atomic_t count;/*引用計數器,0允許執行,否則禁止*/
void (*func)(unsigned long);/*執行函數*/
unsigned long data;//func的參數
};
調度:每個處理器有tasklet_vec和tasklet_hi_vec結構,分別為低、高優先級tasklet_strucu鏈表
由tasklet_schedule()和tasklet_hi_schdule()進行調度
檢查tasklet是否為TASKLET_STATE_SCHED.如果是返回 調用_tasklet_schedule 保存中斷狀態,禁止本地中斷 把需要調度的處理器加到tasklet_vec 或tasklet_hi_vec鏈表的頭上 喚起TASKLET_SOFTIRQ或TASKLET_HI_SOFTIRQ軟中斷 恢復中斷狀態並返回
軟中斷處理程序:
tasklet_action(),tasklet_hi_action()的操作
禁止中斷 將當前處理器置為null,清空鏈表 允許中斷 循環遍歷鏈表每一個待處理的tasklet
如果是多處理器系統,判斷是否TASKLET_STATE_SCHED,如果在運行,跳過。 如果未在執行,設置TASKLET_STATE_RUN。 檢查count==0,否則tasklet被禁止,跳過。 執行tasklet,清空TASKLET_STATE_RUN標志 執行下一個tasklet
使用tasklet
聲明tasklet:
DECLARE_TASKLET(name,func,data)DECLARE_TASKLET_DISABLED(.)
tasklet_init(t, tasklet_handler, dev); 編寫處理程序:因為是依靠軟中斷實現的,處理程序不能睡眠。Tasklet允許響應中斷。 調用tasklet_schedule(&my_tasklet);調度tasklet,實際上是標記/掛起,只要有機會,my_tasklet就會盡快執行。
tasklet_disable(&my_tasklet); tasklet_enable(&my_tasklet);禁止和激活
折衷
頻繁中斷或tasklet頻繁發生的時候:盡快處理,用戶進程得不到響應;滯後執行,中斷處理也不快。
==》》使用低優先級核心進程專門處理軟中斷ksoftirqd/n
for(;;){
if(!softirq_pending(cpu)){
schedule();
}
set_current_state(TASK_RUNNING);
while(softirq_pending(cpu)){
do_softirq();
if(need_schdule())schedule();
}
}
工作隊列:
將下半部功能交由內核線程執行,有著線程上下文環境,可以睡眠。
提供創建worker threads的接口,提供接口把需要推後執行的任務排到隊列裡,提供默認的工作者線程處理排到隊列裡的下半部工作。
實現:
數據結構
每種工作者線程有一個workqueue_struct結構 裡面有NR_CPUS個cpu_work_queue_strcut對應於每個處理器的一個工作者線程 工作用work_struct表示,含有一個執行函數fuc,每個cpu的工作線程都對應一個work_struct鏈表。
worker_thread()的核心,是一個死循環
線程將自己放置為休眠狀態 若鏈表為空,休眠 不為空,調用run_workqueue函數執行工作。
鏈表不為空時,循環執行
選取下一個節點對象,獲取執行函數和參數 待處理標志清0 調用函數 重復執行
使用:
創建推後的工作
DECLARE_WORK(name, void(func)(void), void*data);靜態
INIT_WORK(strut work_struct* task,…);動態 工作隊列處理函數
運行於進程上下文,允許響應中斷,不持有鎖,可以睡眠。不能訪問用戶空間 對工作進行調度
schedule_work(&work);提交給工作者進程
schedule_delay_work(&work,delay) 刷新操作
flush_scheduled_work(),函數等待隊列中所有對象都被執行以後返回 創建新的工作隊列
如果缺省的隊列不能滿足你的需要,你應該創建新的工作隊列和與之相對應的工作線程。
各種機制的比較
下半部
上下文
順序執行保障
軟中斷
中斷
沒有
Tasklet
中斷
同類型不能同時執行
工作隊列
進程
沒有(和進程上下文一樣,被調度)
如果任務需要推後到進程上下文完成,有休眠的需要 工作隊列
任務隊列接口簡單,同種類型不能同時執行 tasklet
軟中斷提供的執行序列化的保障最少,必須格外小心采取一些步驟確保共享數據