linux内核对外部中断的处理分成两大部分HARDIRQ和SOFTIRQ,HARDIRQ在执行时处理器的中断是关闭的,耗时的工作延迟到SOFTIRQ中执行。因此内核为驱动提供了一个基于SOFTIRQ的任务延迟的实现机制tasklet。
tasklet是内核定义的几种softirq之一,根据优先级不同,内核将tasklet分成两种,在softirq中对应TASKLET_SOFTIRQ和HI_SOFTIRQ,后者的执行优先级高于前者。
linux系统初始化期间通过调用softirq_init为TASKLET_SOFTIRQ和HI_SOFTIRQ安装了执行函数。
void __init softirq_init(void)
{
int cpu;
for_each_possible_cpu(cpu)
{
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;
}
open_softirq(TASKLET_SOFTIRQ, tasklet_action);
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
在中断处理SOFTIRQ中,如果发现本地CPU的_softirq_pending上TASKLET_SOFTIRQ或者HI_SOFTIRQ位被置1,就调用tasklet_action或者tasklet_hi_action。
驱动中为了实现基于tasklet机制的延迟操作,需要声明一个tasklet对象。可以用DECLEARE_TASKLET宏声明并初始化一个静态的tasklet对象,也可以调用tasklet_init来完成。
声明了tasklet对象之后,驱动需要调用tasklet_schedule来向系统提交这个tasklet,实际上就是将一个tasklet对象加入到tasklet_vec管理的链表中。
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);
}
通过操作tail将tasklet对象依次加入到链表中。
如果一个tasklet被调度执行完后,其state的TASKLET_STATE_SCHED位被清0,这意味着除非被再次提交,否则下次的SOFTIRQ将不会再调度到它,这是一种one-shot特性。
如果tasklet可以被提交,那么接下来的工作就是把它加入到当前处理器tasklet_vec管理的链表中,然后再通过raise_softirq_irqoff调用告诉SOFTIRQ当前处理器有个TASKLET_SOFTIRQ正等待处理。raise_softirq_irqoff用一个整形变量的位来表示该位上是否有待决的softirq等待处理。
阅读(844) | 评论(0) | 转发(0) |