tasklet
<h2>定义</h2>
<pre><code class="language-c">// file: linux-2.6.11\kernel\softirq.c
/* Some compilers disobey section attribute on statics when not
initialized -- RR */
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL };
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL };
/* Tasklets */
struct tasklet_head
{
struct tasklet_struct *list;
};
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count; // 锁计数器
void (*func)(unsigned long);
unsigned long data;
};</code></pre>
<p>tasklet 是基于 <code>HI_SOFTIRQ</code> 和 <code>TASKLET_SOFTIRQ</code> 这 2 个类型的软中断基础之上。tasklet_vec (注意是每 CPU 变量)的元素是 <code>tasklet_head</code>,它指向一个 <code>tasklet</code> 链表,里面存放各种任务。</p>
<h2>tasklet 流程</h2>
<pre><code class="language-c">// file: kernel/softirq.c
void __init softirq_init(void)
{
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}</code></pre>
<p>系统初始化的时候,设置 2 个软中断</p>
<pre><code class="language-c">// file: E:\ProgramWork\linux\linux-2.6.11\kernel\softirq.c
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;
}</code></pre>
<p>初始化函数很简单。</p>
<pre><code class="language-c">// file: E:\ProgramWork\linux\linux-2.6.11\include\linux\interrupt.h
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
atomic_inc(&t->count);
smp_mb__after_atomic_inc();
}
static inline void tasklet_disable(struct tasklet_struct *t)
{
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic_dec();
atomic_dec(&t->count);
}</code></pre>
<p>对 tasklet 禁用和启用。本质上是通过设备其 count 字段来实现。</p>
<pre><code class="language-c">// file: include/linux/interrupt.h
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
// file: kernel/softirq.c
void fastcall __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags); // 保存 IF 标记,并禁用中断
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags); // 恢复
}</code></pre>
<p>激活 tasklet,将其加入 list 队头。</p>
<pre><code class="language-c">// file: E:\ProgramWork\linux\linux-2.6.11\kernel\softirq.c
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = NULL;
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) { // 检查 state 是否有 STATE_RUN 标记。表示在另一个 CPU 上运行?
if (!atomic_read(&t->count)) { // count 表示 tasklet 是否被 disable
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t); // 这里会清 STATE_RUN 标记
}
local_irq_disable();
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
__raise_softirq_irqoff(TASKLET_SOFTIRQ); // 仅仅设置 pending 位图
local_irq_enable();
}
}
static inline int tasklet_trylock(struct tasklet_struct *t)
{
return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}
</code></pre>
<p>软中断响应函数,就是按照 list 依次执行任务。先将全局 listA 保存到局部 listB,然后清空 listA 以便继续接受新的 task 加入。然后遍历 listB,如果已经有 STATE_RUN 状态,说明有其它人 (谁?) 在执行,则本次忽略本次执行,将 task 加入到 listA 中,也就是下次会执行的全局 list ;另外,如果 task 被 disable,也会忽略本次执行,加入到 listB 中。</p>