軟中斷被執行的優先級要高於內核線程。硬中斷是可以搶佔內核線程的,硬中斷退出時會立即執行軟中斷。這時軟中斷執行程序是運行在中斷上下文的。如果軟中斷執行程序在指定時間內沒處理完,就會掛起來等下次下次被執行。下次被執行可以是另一個硬中斷退出時在中斷上下文中執行,也可以是在特殊的內核線程ksoftirq被調度到來執行,這時是運行在線程上下文的。
總體來說,軟中斷執行程序被執行的機會會比普通線程要多。所以一些要優先並需要及時處理的工作可以交給軟中斷來處理。但linux 實現了軟中斷,但對內核模塊開發的人員來說,內核並沒有直接提供使用軟中斷的API。內核提供了tasklet 來給內核模塊開發人員來用。
tasklet是在軟中斷HI_SOFTIRQ和TASKLET_SOFTIRQ基礎上實現的。
初始化:
在start_kernel()裏調用softirq_init()初始化這兩個軟中斷。
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
數據結構:
每個tasklet 由數據結構tasklet_struct 代表。
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state; //tasklet狀態。
atomic_t count; //鎖計數器
void (*func)(unsigned long); //tasklet處理函數。
unsigned long data; //處理函數需要的參數。
};
全局數組tasklet_vec[NR_CPU] 和 tasklet_hi_vec[NR_CPU] 。數組元素是一個tasklet_head元素,是tasklet_struct 鏈表頭。數組下標對應每個cpu ID.即數組保存了每個cpu上的tasklet_struct 鏈表。
Tasklet 的state狀態字段有如下狀態:
TASKLET_STATE_SCHED | 表示tasklet已經插入到tasklet_vec 或 tasklet_hi_vec數組中的一個鏈表上了。 |
TASKLET_STATE_RUN | 表示tasklet正在被執行。 |
static inline int tasklet_trylock(struct tasklet_struct *t)
{
return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}
static inline void tasklet_unlock(struct tasklet_struct *t)
{
smp_mb__before_clear_bit();
clear_bit(TASKLET_STATE_RUN, &(t)->state);
}
調用tasklet_init()來定義一個tasklet.
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),
unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}
禁止tasklet:
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
/*禁止tasklet後立即返回*/
atomic_inc(&t->count); //增加tasklet的鎖計數器。
smp_mb__after_atomic_inc();
}
static inline void tasklet_unlock_wait(struct tasklet_struct *t)
{
/*直到等到tasklet執行完畢返回*/
while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
}
static inline void tasklet_disable(struct tasklet_struct *t)
{
/*禁止tasklet並且直到tasklet執行完畢後返回*/
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
激活tasklet:
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic_dec();
/*遞減tasklet的鎖計數器*/
atomic_dec(&t->count);
}
調度tasklet
根據tasklet的優先級調用tasklet_schedule() 或tasklet_hi_schedule().
static inline voidtasklet_schedule(struct tasklet_struct *t)
{
/*如果tasklet沒被調度過,即沒被插入tasklet_vec相應的鏈表 ,調度*/
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
{
__tasklet_schedule(t);
}
}
void fastcall__tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
/*保存中斷狀態寄存器並關閉本地CPU的中斷*/
local_irq_save(flags);
/*把tasklet插入本地CPU的tasklet_vec中對應的鏈表裏*/
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
/*把本地CPU的軟中斷TASKLET_SOFTIRQ位標記爲掛起*/
raise_softirq_irqoff(TASKLET_SOFTIRQ);
/*恢復中斷狀態寄存器並開本地CPU中斷*/
local_irq_restore(flags);
}
軟中斷TASKLET_SOFTIRQ的處理函數如下:
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
/*保存中斷狀態寄存器並關閉本地CPU的中斷*/
local_irq_disable();
取得tasklet_vec數組中本地CPU的tasklet鏈表,並存入臨時變量中*/
list = __get_cpu_var(tasklet_vec).list;
/*清空tasklet_vec數組中本地CPU的tasklet鏈表*/
__get_cpu_var(tasklet_vec).list = NULL;
/*恢復中斷狀態寄存器並開本地CPU中斷*/
local_irq_enable();
/*循環執行tasklet鏈表上每個tasklet的處理函數*/
while (list)
{
/*從鏈表上摘下一個tasklet*/
struct tasklet_struct *t = list;
list = list->next;
/*如果tasklet沒在被執行,執行,設置tasklet 的state字段爲RUNNING狀態*/
if (tasklet_trylock(t))
{
/*如果tasklet的鎖計數器爲0,執行*/
if (!atomic_read(&t->count))
{
/*清除tasklet的SCHED狀態*/
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
/*執行tasklet的處理函數*/
t->func(t->data);
/*清除tasklet 的state字段的RUNNING狀態,繼續處理鏈表上的下一 個tasklet*/
tasklet_unlock(t);
continue;
}
/*如果tasklet 的鎖計數器不爲0,表示tasklet被禁用,清除state字段的RUNNING狀態*/
tasklet_unlock(t);
}
/*關閉本地CPU中斷,並把以上沒被處理的tasklet重新掛到tasklet_vec數組中對應本地CPU上的鏈表上*/
local_irq_disable();
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
/*把本地CPU上的TASKLET_SOFTIRQ標記爲掛起,並使能中斷*/
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
本文出自 “耀洋的博客” 博客,請務必保留此出處http://yaoyang.blog.51cto.com/7657153/1261841