微线程(tasklet)是一种更通用的下半部机制,大多数情况下应该优先使用微线程,只有在对性能要求非常高的时候才考虑使用软中断。然而,微线程是基于软中断的,它实际上是一个软中断。内核中的微线程用两个软中断表示:HI_SOFTIRQ和TASKLET_SOFTIRQ。两者唯一的区别在于HI_SOFTIRQ优先级要高些。
数据结构
struct tasklet_struct { |
|
struct tasklet_struct *next; |
/*next tasklet in the list */ |
unsigned long state; |
/*state of the tasklet */ |
atomic_t count; |
/*reference counter */ |
void(*func)(unsigned long); |
/*tasklet handler function */ |
unsigned long data; |
/*argument to the tasklet function */ |
}; |
|
状态state的值可为0,TASKLET_STATE_SCHED,TASKLET_STATE_RUN。TASKLET_STATE_SCHED表示某个微线程将被调度运行,而TASKLET_STATE_RUN表示某个微线程正在运行。
count为微线程的引用计数,为非0时表示微线程被禁用,不能运行。为0时表示微线程可以运行,且如果标记为未决状态,将可以运行。
调度微线程
被调度的微线程存储于两个每-CPU结构中:tasklet_vec(对于普通微线程)和tasklet_hi_vec(对于高优先级微线程)。这两个结构都是tasklet_struct结构构成的链表。链表表中的每个结点代表不同的微线程。调度微线程分别采用tasklet_schedule()和tasklet_hi_schedule()。大致步骤如下:
1.检查微线程的状态是否为TASKLET_STATE_SCHED。如果是,该微线程已经被调度,函数立即返回。
2.调用__tasklet_schedule()。
3.保存中断系统的状态,然后禁用所有本地中断。
4.将被调度的微线程添加到tasklet_vec或tasklet_hi_vec链表的头部。这些链表对每个处理器来说是唯一的。
5.触发TASKLET_SOFTIRQ和HI_SOFTIRQ软中断,这样使得do_softirq()能在将来的某个时刻执行该微线程。(实质上就是将微线程标记为一个未决的软中断)
6.恢复中断到之前的状态,然后返回。
这样,do_softirq()在某个时刻会运行这些未决的软中断,并执行相关的处理函数,即tasklet_action()和tasklet_hi_action()。这两个函数是微线程处理的核心。执行的步骤如下:
1.禁用本地中断,获取本处理器上的tasklet_vec和tasklet_hi_vec链表。
2.清除链表。
3.启动本地中断。
4.遍历链表中的每个未决微线程。
5.如果是处理器空闲,检测微线程是否运行于另外一个处理器,即检测TASKLET_STATE_RUN标志。如果是,跳过,处理下一个微线程。
6.如果否,设置TASKLET_STATE_RUN,这样就不会在另一个处理器上运行。
7.检测微线程的引用数是否为0,以确认微线程是否使能。如果否,跳过,处理下一个微线程。
8.执行微线程处理函数。
9.清除微线程state域的TASKLET_STATE_RUN标记。
10.重复上述过程,直到完毕。
使用微线程
静态声明
DECLARE_TASKLET(name,func, data)
DECLARE_TASKLET_DISABLED(name,func, data);
例子如下:
DECLARE_TASKLET(my_tasklet,my_tasklet_handler, dev);
等价于:
struct tasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0),
my_tasklet_handler,dev };
动态声明
tasklet_init(t,tasklet_handler, dev); /* dynamically as opposed to statically */
编写自己的微线程处理函数
函数原型:void tasklet_handler(unsigned long data)
跟软中断一样,微线程不能睡眠,所以不能使用户信号量等机制。
调度微线程
调度微线程使用如下函数:
tasklet_schedule(&my_tasklet);/* mark my_tasklet as pending */
其实质就是将微线程标记为未决状态。
通过函数tasklet_disable()来禁用一个微线程,如果被禁用的微线程正在运行,函数将等待微线程执行完毕后,返回。函数tasklet_disable_nosync()将立即返回。函数tasklet_enable()使能一个微线程。
tasklet使用模板
UsingTasklets to Offload Work from Interrupt Handlers |
struct roller_device_struct{ /*… */ struct tasklet_struct tsklt; /*… */ };
void__init roller_init() { struct roller_device_struct *dev_struct; /*… */ /*Initialize tasklet */ tasklet_init(&dev_struct->tsklt,roller_analyze, dev); }
/*The bottom half */ void roller_analyze() { /*… */ }
/*The interrupt handler */ static irqreturn_t roller_interrupt(int irq, void *dev_id) { /*… */ /*Mark tasklet as pending */ tasklet_schedule(&dev_struct->tsklt); return IRQ_HANDLED; } |