软中断和tasklet
.6 的linux kernel最多可以使用32中softirq,见代码:
- softirq.c
- /*表示softirq最多可以有32种类型,实际上linux只使用了六种,见文件interrupt.h*/
- static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
linux中实际使用的六种:
- interrupt.h
- enum
- {
- HI_SOFTIRQ=0, /*用于高优先级的tasklet*/
- TIMER_SOFTIRQ, /*用于定时器的下半部*/
- NET_TX_SOFTIRQ,/*用于网络层发包*/
- NET_RX_SOFTIRQ, /*用于网络层收报*/
- SCSI_SOFTIRQ, /*用于scsi设备*/
- TASKLET_SOFTIRQ /*用于低优先级的tasklet*/
- };
对于softirq,linux kernel中是在中断处理程序执行的,具体的路径为:
- do_IRQ() --> irq_exit() --> invoke_softirq() --> do_softirq() --> __do_softirq()
在__do_softirq()中有这么一段代码:
- do {
- if (pending & 1) {
- h->action(h);
- rcu_bh_qsctr_inc(cpu);
- }
- h++;
- pending >>= 1;
- } while (pending);
你看,这里就是对softirq进行处理了,因为pengding是一个__u32的类型,所以每一位都对应了一种softirq,正好是32种(linux kernel中实际上只使用了前6种 ).
h->action(h),就是运行softirq的处理函数。
对于tasklet,前面已经说了,是一种特殊的softirq,具体就是第0和第5种softirq,所以说tasklet是基于softirq来实现的。
tasklet既然对应第0和第5种softirq,那么就应该有对应的处理函数,以便h->action()会运行tasklet的处理函数。
我们看代码:
- softirq.c
- void __init softirq_init(void)
- {
- open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
- open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
- }
这里注册了两种tasklet所在的softirq的处理函数,分别对应高优先级的tasklet和低优先级的tasklet。
我们看低优先级的吧(高优先级的也一样)。
- 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)) {
- 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 = __get_cpu_var(tasklet_vec).list;
- __get_cpu_var(tasklet_vec).list = t;
- __raise_softirq_irqoff(TASKLET_SOFTIRQ);
- local_irq_enable();
- }
- }
你看,在运行softirq的处理时(__do_softirq),对于
- do {
- if (pending & 1) {
- h->action(h);
- rcu_bh_qsctr_inc(cpu);
- }
- h++;
- pending >>= 1;
- } while (pending);
如果tasklet有任务需要处理,会运行到h->action(),这个函数指针就会指向tasklet_action(),然后在tasklet_action()里再去执行tasklet对应的各个任务,这些任务都是挂在一个全局链表里面的,具体的代码这里就不分析了。
另外, softirq在smp中是可能被同时运行的,所以softirq的处理函数必须被编写成可重入的函数。
但tasklet是不会在多个cpu之中同时运行的,所以tasklet的处理函数可以编写成不可重入的函数,这样就减轻了编程人员的负担。