linux 內核學習 tasklet 詳解

軟中斷被執行的優先級要高於內核線程。硬中斷是可以搶佔內核線程的,硬中斷退出時會立即執行軟中斷。這時軟中斷執行程序是運行在中斷上下文的。如果軟中斷執行程序在指定時間內沒處理完,就會掛起來等下次下次被執行。下次被執行可以是另一個硬中斷退出時在中斷上下文中執行,也可以是在特殊的內核線程ksoftirq被調度到來執行,這時是運行在線程上下文的。


總體來說,軟中斷執行程序被執行的機會會比普通線程要多。所以一些要優先並需要及時處理的工作可以交給軟中斷來處理。但linux 實現了軟中斷,但對內核模塊開發的人員來說,內核並沒有直接提供使用軟中斷的API。內核提供了tasklet 來給內核模塊開發人員來用。


tasklet是在軟中斷HI_SOFTIRQTASKLET_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

發佈了17 篇原創文章 · 獲贊 7 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章