首页 > 代码库 > Linux中断下半部tasklet机制

Linux中断下半部tasklet机制

平台:Linux2.6.18

一,      软中断

1.1         在文件<linux/interrupt.h>中

1.1.1     当前内核用到的软中断类型

1 enum2 {    // 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_SOFTIRQ9 };

1.1.2     软中断结构体softirq_action

1 struct softirq_action2 {  //如果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 =http://www.mamicode.com/ 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 enum10 {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_head3 {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机制数据结构间的关联

 

Linux中断下半部tasklet机制