Linux 中断子系统(六)Tasklet
原图
为什么有 tasklet
linux kernel 已经把中断处理分成了 top half 和 bottom half,看起来已经不错了,那为何还要提供 softirq、tasklet 和 workqueue 这些 bottom half 机制。
workqueue 和 softirq、tasklet 有本质的区别:
- workqueue 运行在 process context,而 softirq 和 tasklet 运行在 interrupt context。因此,出现 workqueue 是不奇怪的,在有 sleep 需求的场景中需要。
- softirq 更倾向于性能。软中断的种类是编译时静态定义的,在运行时不能添加或删除,同一种软中断的处理函数可以在多个处理器上同时执行,处理函数必须是可以重入的,需要使用锁保护临界区。
- tasklet 更倾向于易用性。Tasklet 可以在运行时添加或删除,一个 Tasklet 同一时刻只能在一个处理器上执行,不要求处理函数是可以重入的。
数据结构
Tasklet 的数据结构如下:
1 | <include/linux/interrupt.h> |
成员 next 用来把 Tasklet 添加到单向链表中。 成员 state 是 Tasklet 的状态,取值如下:
- 0: Tasklet 没有被调度
- (1 << TASKLET_STATE_SCHED): Tasklet 被调度,即将被执行
- (1<< TASKLET_STATE_RUN):只在多处理器系统中使用,表示 Tasklet 正在执行
成员 count 是计数,0 表示允许 Tasklet 被执行,非零值表示禁止 Tasklet 被执行成员 func 是处理函数,成员 data 是传给处理函数的参数。 每个处理器有两条单向链表:低优先级 Tasklet 链表和高优先级 Tasklet 链表。
1
2
3
4
5
6
7
<kernel/softirq.c>
struct tasklet_head {
struct tasklet_struct *head;
struct tasklet_struct **tail;
};
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
1 | <kernel/softirq.c> |
实现(扩展🙈)
Tasklet 是基于软中断实现的,根据优先级分为两种,低优先级 Tasklet 和高优先级 Tasklet。软中断 HI_SOFTIRO 执行高优先级 Tasklet,软中断 TASKLET_SOFTIRQ 执行低优先级 Tasklet。
调度 Tasklet
函数 tasklet_schedule() 用来调度低优先级 Tasklet,函数 tasklet_hi_schedule() 用来调度高优先级 Tasklet。以函数 tasklet_schedule() 为例说明,其代码如下
1 | <include/linux/interrupt.h、kernel/softirq.c> |
如果 Tasklet 没有被调度过,那么首先设置调度标志位,然后把 Tasklet 添加到当前处理器的低优先级 Tasklet 链表的尾部,最后触发软中断 TASKLET_SOFTIRQ。
执行 Tasklet
初始化的时候,把软中断 TASKLET_SOFTIRQ 的处理函数注册为函数 tasklet_action,把软中断 HI_SOFTIRQ 的处理函数注册为函数 tasklet_hi_action()。
1 | <kernel/softirq.c> |
以函数 tasklet_action() 为例说明,其代码如下
1 | <kernel/softirq.c> |
在上面的注释 4 如果 Tasklet 的计数为 0,表示允许 Tasklet 被执行 ,tasklet 就是通过这个来保证只在一个 cpu 上执行。如果该 tasklet 已经在别的 cpu 上执行了,那么我们将其挂入该 cpu 的 tasklet 链表的尾部,这样,在下一个 tasklet 执行时机到来的时候,kernel 会再次尝试执行该 tasklet,在这个时间点,也许其他 cpu 上的该 tasklet 已经执行完毕了。通过这样代码逻辑,保证了特定的 tasklet 只会在一个 cpu 上执行,不会在多个 cpu 上并发。
参考文献
http://www.wowotech.net/irq_subsystem/soft-irq.html
http://www.wowotech.net/irq_subsystem/tasklet.html
《Linux 内核深度解析》