今天看了下tasklet,重點分析了其和軟中斷的關系,特此記錄
關於軟中斷,在之前的中斷文章中已經有所介紹,這里就不多說了,只是說明下,系統中默認支持32種軟中斷,而實際上系統定義的軟中斷僅有以下幾種。
enum { HI_SOFTIRQ=0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS };
實際上並沒有什么關系,只有中樞的內核代碼才使用軟中斷,而如果用戶想要使用這種方式,直接使用軟中斷並不是一個好的選擇,內核為用戶提供了另外一種方便的方式即tasklet,tasklet本質上也是一種軟中斷,准確來說是系統從軟中斷類型中拿出一種來支持tasklet,所以tasklet就是一種軟中斷,不過在軟中斷的基礎上,tasklet進行了更細的划分。每個CPU維護一個tasklet鏈表,其中保存當前CPU所有注冊的tasklet。由於tasklet本質上仍然是軟中斷,所以其處理方式依賴於軟中斷的處理時機,在系統檢查處理軟中斷時,檢查到tasklet類型的軟中斷,調用tasklet_action函數進行處理。
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
每個tasklet由一個tasklet_struct結構描述
struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); unsigned long data; };
所有的tasklet通過next連接成一個局部於CPU的鏈表,注意該結構中有個函數指針func,表示處理該tasklet的指針。所以要注冊tasklet還需要提供處理函數,軟中斷的處理函數已經由系統定義好的。通過tasklet_schedule函數可以注冊一個tasklet到系統
static inline void tasklet_schedule(struct tasklet_struct *t) { if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) __tasklet_schedule(t); }
void __tasklet_schedule(struct tasklet_struct *t) { unsigned long flags; local_irq_save(flags); t->next = NULL; *__this_cpu_read(tasklet_vec.tail) = t; __this_cpu_write(tasklet_vec.tail, &(t->next)); raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_restore(flags); }
實際的工作就比較簡單,實際上就是插入到CPU維護的tasklet鏈表的尾部。然后會調用raise_softirq_irqoff標記軟中斷位圖。這樣在下次處理軟中斷的時候,就會處理tasklet,進而處理注冊的tasklet。在操作CPU變量期間會禁用本地中斷。
明白了軟中斷和tasklet的關系,還需要注意:
1、軟中斷支持在不同CPU上並行運行,不管是同種類型的還是不同類型的。
2、tasklet僅僅支持不同類型的在不同CPU上並行運行,同種類型的不支持。
參考:
1、linux內核3.10.1源碼
2、深入linux內核架構