首先要说的是,tasklet也是在软中断处理过程中完成的。表示一个软中断的结构如下,可以看出,里面是一个函数指针,顾函数名而知其意,就是软中断时的行为,也就是软中断时要做的是什么,由一个函数指针来自由定义。当然,内核已经帮我们定义了一些。
struct softirq_action
{
void (*action)(struct softirq_action *);
};
使用软中断前要先注册,这和中断处理有点类似,为什么要注册呢?因为在中断时要完成的动作需要我们来定义,比如你要处理网卡接受的数据,内核并不知道要这样做,你要注册一下,让内核清楚这个软中断具体要做些什么。(注:最多可以注册32个软中断)
那么如何注册呢?
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
open_softirq函数用于注册,可以看出此函数无他,就是把一个数组里的第nr项的函数指针赋值为你所需要的那个函数指针,就此,注册完毕。
仅仅注册了,还不够,如果要使用,我们要先唤醒这个被注册了的softirq,那么就需要raise_softirq(名字就叫唤醒软中断,很好记),可以看出这个函数就是唤醒了那个你刚才注册在数组里的第nr项的处理函数,等处理中断时,这个函数就会被执行。
void raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
这里有解释一下里面调用的几个函数
1.local_irq_save 这个函数做了什么呢,看看源码里是这样写的。
#define local_irq_save(flags) \
do { \
raw_local_irq_save(flags);\
} while (0) 里面调用了raw_local_irq_save,go on ->
里面还调用了不少,关键函数是arch_local_irq_save,顾名思义,该函数与架构有关,那么就看看x86下是个什么节奏。
static inline notrace unsigned long arch_local_irq_save(void)
{
unsigned long flags = arch_local_save_flags();
arch_local_irq_disable();
return flags;
}
可看出,关掉了本地中断。
2. raise_softirq_irqoff函数
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (!in_interrupt())
wakeup_softirqd();
}
看看里面的注释,哦,是这样啊,如果我们在处理完一个中断或者软中断时,我们会运行软中断或者处于中断中我们要唤醒ksoftirqd线程来在之后处理这个软中断。
以上都是为软中断触发前做的准备,下面就要触发软中断了也就是__do_softirq,这里不急,我们先说一下tasklet。
tasklet是通过软中断实现的,由tasklet_struct结构表示。 由tasklet_schedule和 tasklet_hi_schedule来调度。
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);
}
这里拿tasklet_schedule来举例,里面调用了__tasklet_schedule,其实调度,说的简单一点,就是把这个tasklet_struct的实例插入到tasklet_vec的链表的尾部,然后在之后的操作中就会执行这个tasklet了。那么,为什么插入到链表中就会执行了呢?请看下面
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __this_cpu_read(tasklet_vec.head);
__this_cpu_write(tasklet_vec.head, NULL);
__this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
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_enable();
}
}
还记得之前的softirq_action么,也就是注册软中断时的行为的一个结构体。这里的tasklet_action就符合其要求。
的确,内核也通过open_softirq(TASKLET_SOFTIRQ, tasklet_action); 注册了它,那么在触发tasklet时就会执行该代码,里面就是把list里的tasklet_struct执行一遍,直到list为空,那么之前使用tasklet_schedule挂进list的tasklet_struct也会被执行,就相当于被调度了。
最后,让我们来看看关键代码,do_softirq里有些什么吧。
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu;
pending = local_softirq_pending();
account_system_vtime(current);
__local_bh_disable((unsigned long)__builtin_return_address(0),
SOFTIRQ_OFFSET);
lockdep_softirq_enter();
cpu = smp_processor_id();
restart:
/* Reset the pending bitmask before enabling irqs */
set_softirq_pending(0);
local_irq_enable();
h = softirq_vec;
do {
if (pending & 1) {
unsigned int vec_nr = h - softirq_vec;
int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(vec_nr);
trace_softirq_entry(vec_nr);
h->action(h);
trace_softirq_exit(vec_nr);
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %u %s %p"
"with preempt_count %08x,"
" exited with %08x?\n", vec_nr,
softirq_to_name[vec_nr], h->action,
prev_count, preempt_count());
preempt_count() = prev_count;
}
rcu_bh_qs(cpu);
}
h++;
pending >>= 1;
} while (pending);
local_irq_disable();
pending = local_softirq_pending();
if (pending && --max_restart)
goto restart;
if (pending)
wakeup_softirqd();
lockdep_softirq_exit();
account_system_vtime(current);
__local_bh_enable(SOFTIRQ_OFFSET);
}
h = softirq_vec;
h->action(h); 执行链表里的action。