Linux中断处理( 三 )


所以,do_softirq() 函数首先通过 softirq_mask(cpu) 来获取当前CPU对应被屏蔽的softirq,而 softirq_active(cpu) & mask 就是获取需要执行的softirq,然后就通过对比 __softirq_active 字段的各个位来判断是否要执行该类型的softirq 。
tasklet机制前面说了,tasklet机制是基于softirq机制的,tasklet机制其实就是一个任务队列,然后通过softirq执行 。在Linux内核中有两种tasklet,一种是高优先级tasklet,一种是普通tasklet 。这两种tasklet的实现基本一致,唯一不同的就是执行的优先级,高优先级tasklet会先于普通tasklet执行 。
tasklet本质是一个队列,通过结构体 tasklet_head 存储,并且每个CPU有一个这样的队列,我们来看看结构体 tasklet_head 的定义:
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;};从 tasklet_head 的定义可以知道,tasklet_head 结构是 tasklet_struct 结构队列的头部,而 tasklet_struct 结构的 func 字段正式任务要执行的函数指针 。Linux定义了两种的tasklet队列,分别为 tasklet_vec 和 tasklet_hi_vec,定义如下:
struct tasklet_head tasklet_vec[NR_CPUS];struct tasklet_head tasklet_hi_vec[NR_CPUS];可以看出,tasklet_vec 和 tasklet_hi_vec 都是数组,数组的元素个数为CPU的核心数,也就是每个CPU核心都有一个高优先级tasklet队列和一个普通tasklet队列 。
调度tasklet如果我们有一个tasklet需要执行,那么高优先级tasklet可以通过 tasklet_hi_schedule() 函数调度,而普通tasklet可以通过 tasklet_schedule() 调度 。这两个函数基本一样,所以我们只分析其中一个:
static inline void tasklet_hi_schedule(struct tasklet_struct *t){ if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { int cpu = smp_processor_id(); unsigned long flags; local_irq_save(flags); t->next = tasklet_hi_vec[cpu].list; tasklet_hi_vec[cpu].list = t; __cpu_raise_softirq(cpu, HI_SOFTIRQ); local_irq_restore(flags); }}函数参数的类型是 tasklet_struct 结构的指针,表示需要执行的tasklet结构 。tasklet_hi_schedule() 函数首先判断这个tasklet是否已经被添加到队列中,如果不是就添加到 tasklet_hi_vec 队列中,并且通过调用 __cpu_raise_softirq(cpu, HI_SOFTIRQ) 来告诉softirq需要执行 HI_SOFTIRQ 类型的softirq,我们来看看 __cpu_raise_softirq() 函数的实现:
static inline void __cpu_raise_softirq(int cpu, int nr){ softirq_active(cpu) |= (1<<nr);}可以看出,__cpu_raise_softirq() 函数就是把 irq_cpustat_t 结构的 __softirq_active 字段的 nr位 设置为1 。对于 tasklet_hi_schedule() 函数就是把 HI_SOFTIRQ 位(0位)设置为1 。
前面我们也介绍过,Linux在初始化时会注册两种softirq,TASKLET_SOFTIRQ 和 HI_SOFTIRQ:
void __init softirq_init(){ ... open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);}所以当把 irq_cpustat_t 结构的 __softirq_active 字段的 HI_SOFTIRQ 位(0位)设置为1时,softirq机制就会执行 tasklet_hi_action() 函数,我们来看看 tasklet_hi_action() 函数的实现:
static void tasklet_hi_action(struct softirq_action *a){ int cpu = smp_processor_id(); struct tasklet_struct *list; local_irq_disable(); list = tasklet_hi_vec[cpu].list; tasklet_hi_vec[cpu].list = NULL; local_irq_enable(); while (list != NULL) { struct tasklet_struct *t = list; list = list->next; if (tasklet_trylock(t)) { if (atomic_read(&t->count) == 0) { clear_bit(TASKLET_STATE_SCHED, &t->state); t->func(t->data); // 调用tasklet处理函数 tasklet_unlock(t); continue; } tasklet_unlock(t); } ... }}tasklet_hi_action() 函数非常简单,就是遍历 tasklet_hi_vec 队列并且执行其中tasklet的处理函数 。




推荐阅读