分类: LINUX
2011-01-15 18:05:58
今天继续看第7章《下半部和推后执行的工作》。前天本来已经写了《软中断的实现》,但是没保存,所以今天又重新写一次。
我们首先从头说起。我们一般把中断处理流程切为两个部分或两半。中断处理程序是上半部(top half),对时间要求相对宽松的工作就是下半部(bottom half)的执行目标。
目前,有三种机制可以用来实现将工作推后执行:软中断、tasklet和工作队列。软中断是一种静态定义的下半部接口,有32个,可以在所有处理器上同时执行---即使两个类型相同也可以。tasklet是一种基于软中断实现的灵活性强、动态创建的下半部实现机制。两个不同类型的tasklet可以在不同的处理器上同时执行,但类型相同的tasklet不能同时执行。软中断是在编译期间静态分配的,tasklet却能够被动态的注册或去除。工作对列和它们完全不同。
下面说说软中断的实现。
软中断由softirq_action结构表示,它定义在linux/interrupt.h中:
每个被注册的软中断都占据该数组的一项,因此最多可能有32个中断。
软中断的代码位于kernel/softirq.c中。
这是我第三次写了,昨天没保存只留下上面这些,只好从这里重新开始。再写一次我就快疯了,呜呜!!!
1.软中断处理程序
软中断处理程序action(softirq_action结构中的action函数指针)的函数原型如下:
void softirq_handler(struct softirq_action *)
当内核运行一个软中断处理程序的时候,它就会执行这个action函数,其唯一的参数就是指向对应的softirq_action结构体的指针。为什么要传这个结构体而不是传数据值呢?这样做的原因是可以保证将来在结构体中加入新的域时,无须对所有的软中断处理程序进行变动。如果需要,软中断处理程序可以方便地解析它的参数,从数据成员中提取数值。In my opinion, we can use Macro containof.
一个软中断不会抢占另外一个软中断。实际上,唯一可以抢占软中断的是中断处理程序。不过,其他软中断---甚至是相同类型的软中断---可以在其他处理器上同时执行。
2.执行软中断
一个注册的软中断必须在被标记后才会执行,这就是触发软中断(raising the softirq)。中断处理程序绘制返回前标记它的软中断,使其稍后被执行。(注册软中断和触发软中断在中http://blog.csdn.net/qinzhonghello/archive/2008/11/29/3408420.aspx讲述)
在下面情况中,待处理的软中断会被检查和执行:
1)从一个硬件中断代码处返回时
2)在ksoftirqd内核线程中
3)在那些显式检查和执行待处理的软中断的代码中,如网络子系统
软中断要在do_softirq()函数中执行:
具体分析如下:
1)宏in_interrupt()用来判断内核是否处于中断上下文中,如果是它返回非零,说明内核此刻正在执行中断处理程序,或者正在执行下半部处理程序。定义在
2)局部变量pending保存宏softirq_pending()的返回值。它是待处理的软中断的32位位图,如果第n为被设置为1,那么第n位对应类型的软中断等待处理。
3)函数__do_softirq()
下面这段代码是软中断处理的核心部分:
将指针h指向softirq_vec的第一项,如果pending的第一位被置为1,h->action(h)被调用,指针加1,指向数组中的下一项。位掩码pending右移一位,这样会丢弃第一位,然后让其他各位依次向右移动一个位置。一直重复直到pengding变为0,结束。
4)函数local_irq_save(flags)和local_irq_restore(flags)
用于禁止/激活当前处理器上的本地中断,可以以下语句:
local_irq_disable();
local_irq_enable();
但是,考虑这种情况:如果在调用local_irq_disable()之前已经禁止了中断;如果中断可能在开始时就是关闭的,调用local_irq_enable()将无条件的激活中断。因此,需要有一种机制将中断恢复到以前的状态。函数local_irq_save(flags)在禁止中断之前保存中断系统的状态,local_irq_restore(flags)在准备激活中断时,把中断恢复到它们原来的状态。
这些方法以宏形式出现,因此表面上flags参数(该参数必须被定义为unsiged long 类型)是以值传递的。该参数包含具体体系结构的数据,也就是包含中断系统的状态。至少有一种体系结构把栈信息与值相结合(SPARC),因此flags不能传递给另一个函数(特别是它必须驻留在同一栈帧中)。基于这个原因,对local_irq_save和local_irq_restore()的调用必须在同一个函数中进行。