软中断被执行的优先级要高于内核线程。硬中断是可以抢占内核线程的,硬中断退出时会立即执行软中断。这时软中断执行程序是运行在中断上下文的。如果软中断执行程序在指定时间内没处理完,就会挂起来等下次下次被执行。下次被执行可以是另一个硬中断退出时在中断上下文中执行,也可以是在特殊的内核线程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();

}

}