平台:Linux2.6.18
一, 软中断
1.1 在文件<linux/interrupt.h>中
1.1.1 当前内核用到的软中断类型
1 enum 2 { // HI_SOFTIRQ,TASKLET_SOFTIRQ为tasklet用软中断实现时用到的两个软中断 3 HI_SOFTIRQ=0, 4 TIMER_SOFTIRQ, 5 NET_TX_SOFTIRQ, 6 NET_RX_SOFTIRQ, 7 BLOCK_SOFTIRQ, 8 TASKLET_SOFTIRQ 9 };
1.1.2 软中断结构体softirq_action
1 struct softirq_action 2 { //如果my_softirq指向softirq_vec数组的某项 3 //内核调用软中断处理程序action的方式:my_softirq->action(my_softirq); 4 //传递参数my_softirq对将来在结构体中加入新的域时,可以在不改动action参数形式的情 5 //况下,传递新的域 6 void (*action)(struct softirq_action *); 7 void *data; 8 };
1.2 在文件kernel/softirq.c中
1.2.1 32个软中断数组
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;
1.2.2 软中断处理程序的注册函数open_softirq
1 void open_softirq(int nr, void (*action)(struct softirq_action*), void *data) 2 { 3 softirq_vec[nr].data = data; 4 softirq_vec[nr].action = action; 5 }
1.2.3 __init softirq_init(void)源码
1 void __init softirq_init(void) 2 { 3 open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); 4 open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); 5 }
1.2.4 tasklet是通过软中断实现的
HI_SOFTIRQ与TASKLET_SOFTIRQ是tasklet通过软中断实现的关键,关系如图2-1所示。
图1-1 tasklet通过软中断实现的数据结构之间的联系
tasklet机制创建HI_SOFTIRQ或TASKLET_SOFTIRQ两种类型的软中断,然后,按照软中断的调度过程去执行对应的tasklet处理程序tasklet_hi_action或tasklet_action。
综合以上可以得出结论:tasklet是通过软中断实现的一种下半部机制。
1.2.5 执行软中断do_softirq()
在do_softirq()函数中执行的关键函数是__do_softirq(),以下分析__do_softirq()中部分重要代码。
1 #define MAX_SOFTIRQ_RESTART 10 2 int max_restart = MAX_SOFTIRQ_RESTART; 3 pending = local_softirq_pending();//pending保存软中断的32位位图,类型对应位为1时等待处理 4 restart: 5 /* Reset the pending bitmask before enabling irqs */ 6 set_softirq_pending(0);//先将实际的软中断位图清零。问1:对于tasklet的HI_SOFTIRQ 7 //或TASKLET_SOFTIRQ类型的软中断同时有多个的情况下,这样清零是否合理?当然 8 //是否存在同时有多个HI_SOFTIRQ或TASKLET_SOFTIRQ类型的软中断的情况? 9 //答1:一次do_softirq中调用tasklet_action处理程序时对tasklet_vec整个链表进行处理。 10 local_irq_enable();//后开本地中断 11 h = softirq_vec; 12 do { 13 if (pending & 1) {//类型对应位为1时满足条件,进入处理 14 h->action(h);//在1.2中解释了这种内核调度中断处理程序的方式 15 rcu_bh_qsctr_inc(cpu); 16 } 17 h++; 18 pending >>= 1; 19 } while (pending); 20 local_irq_disable(); 21 pending = local_softirq_pending();//问2:HI_SOFTIRQ与TASKLET_SOFTIRQ是在什么时机触发的? 22 //一次do_softirq()无处理完HI_SOFTIRQ或TASKLET_SOFTIRQ软中断的情况是否存在? 23 //如果存在,如何处理的? 24 //答2:触发的时机,目前了解到的有:tasklet_schedule调度时,以及tasklet_action处理函数中tasklet未被处理时。
25 //问题2,根据下面对tasklet_action处理程序的分析可能确定存在。如何处理的,目前分析的一种可能 26 //的处理方式是:在tasklet_action中未被处理的tasklet放回了tasklet_vec,并且都触发了 27 //TASKLET_SOFTIRQ软中断。所以,在本次do_softirq函数的MAX_SOFTIRQ_RESTART次重复 28 //中,会被继续处理。(Am I right?2014年11月2日) 29 if (pending && --max_restart)//在处理软中断的过程中又有新的软中断挂起了,故再次调度,但 30 //不超过MAX_SOFTIRQ_RESTART次。 31 goto restart;
1.1.1 触发软中断raise_softirq(),raise_softirq_irqoff()
在2.5提到软中断的32位位图,如果第n位被设置为1,那么第n位对应类型的软中断等待处理。而将对应位置1 的处理,正是触发软中断。分析函数raise_softirq(),raise_softirq_irqoff()可知,两个函数执行的核心操作是__raise_softirq_irqoff(nr);然后,分析可知,其完成的功能就是将nr对应的软中断位图置1。
1 #define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0) 2 #define or_softirq_pending(x) (local_softirq_pending() |= (x))
二, tasklet机制
2.1 在文件<linux/interrupt.h>中
2.1.1 tasklet结构体
1 struct tasklet_struct 2 { 3 struct tasklet_struct *next; 4 unsigned long state; 5 atomic_t count; 6 void (*func)(unsigned long); 7 unsigned long data; 8 }; 9 enum 10 { 11 TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ 12 TASKLET_STATE_RUN /* Tasklet is running (SMP only) */ 13 };
2.2 在文件kernel/softirq.c中
2.2.1 两个单处理器数据结构tasklet_vec和tasklet_hi_vec
1 /* Tasklets */ 2 struct tasklet_head 3 { 4 struct tasklet_struct *list; 5 }; 6 //由tasklet_struct结构体构成的链表 7 static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL }; 8 static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL };
2.2.2 调度tasklet
调度函数tasklet_schedule和tasklet_hi_schedule仅有的区别在于分别使用TASKLET_SOFTIRQ和HI_SOFTIRQ,定义在文件<linux/interrupt.h>中,首先检查tasklet的状态是否为TASKLET_STATE_SCHED,如果是已被调度,返回;否则,将TASKLET_STATE_SCHED位置1,然后进入__tasklet_schedule和__tasklet_hi_schedule中,分析__tasklet_schedule如下。
1 void fastcall __tasklet_schedule(struct tasklet_struct *t) 2 { 3 unsigned long flags; 4 5 local_irq_save(flags); 6 t->next = __get_cpu_var(tasklet_vec).list;//将参数t指向的tasklet插入tasklet_vec链表表头 7 __get_cpu_var(tasklet_vec).list = t; 8 raise_softirq_irqoff(TASKLET_SOFTIRQ);//触发TASKLET_SOFTIRQ软中断,对应1.2.5节的问2 9 local_irq_restore(flags); 10 }
2.2.3 tasklet处理程序
处理程序tasklet_action和tasklet_hi_action功能相似,tasklet_action函数的流程如图2-1所示。
图2-1 tasklet_action流程图
三, tasklet通过软中断实现的一些理解
以下以TASKLET_SOFTIRQ类型具体分析。
1)在软中断中定义了TASKLET_SOFTIRQ软中断,在do_softirq函数时调用tasklet_action处理函数,进入tasklet处理程序。
2)在tasklet_action处理函数中,根据图2-1 tasklet_action流程图可知,对tasklet_vec链表中符合如下三个条件的tasklet下半部进行了处理。
三个条件是:⑴TASKLET_STATE_RUN位为0;⑵count=0;⑶TASKLET_STATE_SCHED位为1。含义是调度了的tasklet如何没有在其他处理器上运行且没有被禁止,则进行处理。tasklet_hi_action(tasklet_action)处理程序操作tasklet_hi_vec(tasklet_vec)链表。相关数据结构关系如图3-1所示。
图3-1 软中断与tasklet机制数据结构间的关联