softirq是一种基本的底半部机制,有较强的加锁需求。仅仅在一些对性能敏感的子系统(如网络层、SCSI层和内核定时器等)中才会使用softirq。tasklet建立在softirq之上,使用起来更简单。除非有严格的可扩展性和速度要求,都建议使用tasklet。softirq和tasklet的主要不同是前者是可重用的,而后者不是。softirq的是同实例可运行在不同的处理器上,而tasklet的则不允许。
这里只讨论驱动编写过程中使用更广的tasklet。
tasklet是一个可以在由系统决定的安全时刻在软件中断上下文被调度的特殊函数。它们可以被多次调度运行,但tasklet的调度并不会积累;也就是说,实际只会运行一次,即使在激活tasklet的运行之前重复请求该tasklet的运行也是这样。不会有同一tasklet的多个实例并行地运行,因为它们只运行一次,但是tasklet可以与其他tasklet并行地运行在对称多处理(SMP)系统上。这样,如果驱动程序有多个tasklet,它们必须使用某种锁机制来避免彼此间的冲突。
tasklet可确保和第一次调度它们的函数运行在同样的CPU上。这样,因为tasklet在中断处理例程结束前并不会开始运行,所以此时的中断处理例程是安全的。不管怎么样,在tasklet运行时,当然可以有其他的中断发生,因此在tasklet和中断处理例程之间的锁还是需要的。
taslet的结构和声明:
/* Tasklets --- multithreaded analogue of BHs.
Main feature differing them of generic softirqs: tasklet
is running only on one CPU simultaneously.
Main feature differing them of BHs: different tasklets
may be run simultaneously on different CPUs.
Properties:
* If tasklet_schedule() is called, then tasklet is guaranteed
to be executed on some cpu at least once after this.
* If the tasklet is already scheduled, but its execution is still not
started, it will be executed only once.
* If this tasklet is already running on another CPU (or schedule is called
from tasklet itself), it is rescheduled for later.
* Tasklet is strictly serialized wrt itself, but not
wrt another tasklets. If client needs some intertask synchronization,
he makes it with spinlocks.
*/
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
tasklet的调度:
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
extern void __tasklet_hi_schedule(struct tasklet_struct *t);
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_hi_schedule(t);
}
extern void __tasklet_hi_schedule_first(struct tasklet_struct *t);
举个小例子:
void do_do_tasklet(unsigned long data) {
//做很多耗时的操作
//吧啦吧啦一大堆代码
}
DECLARE_TASKLET(my_tasklet, do_do_tasklet, 0);
/* 中断处理例程 */
irqreturn_t do_do_interrupt(int irq, void *dev) {
//...
tasklet_schedule(&my_tasklet);
my_task_count++;
return IRQ_HANDLED;
}