tasklet是一种软中断,是基于softirq的基础演变而来的,但是由于资源的限制,softirq是在内核里面预定义好的,无法改变其种类,若要改变的话,就要修改内核代码,这对于驱动开发来说不是很合适.因此基于softirq发展出的tasklet来动态的改变中断的种类,这些中断使用链表连接起来,一旦发生了TASKLET_SOFTIRQ中断(tasklet对应的软中断类型),就按照链表的顺序依次全部执行,执行完毕就断开链表.
//首先调用tasklet_init函数,对tasklet_struct 结构体进行初始化
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;
}
struct tasklet_struct
{
struct tasklet_struct *next; //链表指向的下一个目标
unsigned long state;
atomic_t count;
void (*func)(unsigned long); //中断函数
unsigned long data; //传递给中断函数的参数
};
//然后触发tasklet中断,引起调度
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
因为tasklet不同于softirq,softirq允许同一个中断函数在不同的CPU上面执行,但是要保证中断函数的可重入性.tasklet就正好相反,本身就带有锁机制,也就是tasklet_struct的state域,用于同步中断函数的执行,自然就不能同时在多个CPU上面执行了,这样就简化了我们中断函数的编写,不需要考虑到额外的同步问题.至于tasklet_struct的count域是用于屏蔽掉当前tasklet中断,这可以从tasklet_disable这个函数的内容看出.
tasklet既然是一种softirq,那就一定要注册相应的软中断函数:
void __init softirq_init(void)
{
int cpu;
/*因为是哪个处理器触发的软中断就由哪个处理器处理中断函数
* 因此,每个处理器都要添加相应的结构
*/
for_each_possible_cpu(cpu) {
int i;
per_cpu(tasklet_vec, cpu).tail =
&per_cpu(tasklet_vec, cpu).head;
per_cpu(tasklet_hi_vec, cpu).tail =
&per_cpu(tasklet_hi_vec, cpu).head;
for (i = 0; i < NR_SOFTIRQS; i++)
INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
}
register_hotcpu_notifier(&remote_softirq_cpu_notifier);
open_softirq(TASKLET_SOFTIRQ, tasklet_action);
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
上面代码显示tasklet_action就是相应的中断函数
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
/*每个cpu都有一个关于tasklet链表的头和尾,用于中 tasklet中断的处理*/
list = __get_cpu_var(tasklet_vec).head;
__get_cpu_var(tasklet_vec).head = NULL;
__get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
/*检查是否已经被正在执行,即检查(t)->state的TASKLET_STATE_RUN位是否被置位
* 若已置位说明这个中断函数正在被其他的cpu执行
*/
if (tasklet_trylock(t)) {
//检查当前tasklet是否被disable掉了
if (!atomic_read(&t->count)) {
//检查当前的tasklet是否是pending状态,如果是的话,就执行中断函数
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
/*因为上面有一个continue语句,因此对于运行到这里的函数说明上面的判断
* 语句有不满足的情况,第一种情况:当前函数被其他的cpu执行;第二种情况:
* 当前中断被disable了;对于这两种情况都会把他们重新加入到链表的尾部
* 然后重新唤醒TASKLET_SOFTIRQ,等到有空再次执行这些中断函数
*/
local_irq_disable();
t->next = NULL;
*__get_cpu_var(tasklet_vec).tail = t;
__get_cpu_var(tasklet_vec).tail = &(t->next);
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}