日期:2014-05-16  浏览次数:20837 次

浅谈linux2.4 内核中断下半部分(softirq机制)

linux2.4 内核中断下半部分(bottom half)理解(请结合linux2.4内核代码看):

首先先说一下为什么要采用中断下半部分:

中断服务函数大多需要在关中断的情况下去执行,但是有的中断服务函数执行需要较长的时间,如果系统长时间关中断就会让其他的中断得不到响应,所以把需要关中断执行的部分放到中断的上部分,而把对实时性要求不高,不需要关中断执行的操作放到中断下半部分中去。

但是中断下半部分也不能够嵌套执行,否则会复杂化代码实现,所以需要有个实现中断下半部分串行化的机制,即bh机制,bh机制的串行化有两方面:

一是bh函数不能嵌套,二是在多cpu系统中在同一时间只允许一个cpu执行bh函数

由于第二点使bh函数的执行完全串行化了,当有多个cpu的时候,只能一个cpu执行,降低的系统性能。但是如果放宽条件以前的bh函数就不能用了,所以比较好的办法是保留bh,另外在增设一种或几种机制,并把它们纳入统一的框架中,这就是本文要讲的软中断机制。

void __init softirq_init()
{
int i;
/*在这个循环中将bh_task_vec[32]中的.action指针全部初始化为bh_action函数,然后在init_bh()中将bh_action函数里要执行的bh_base[]函数指针数组初始化为具体的函 *数,由数组下标做索引。*/
for (i=0; i<32; i++)

tasklet_init(bh_task_vec+i, bh_action, i);


/*在open_softirq函数中将全局数组变量softirq_vec[].action指针初始化为tasklet_action和tasklet_hi_action函数,数组下标为TASKLET_SOFTIRQ和HI_SOFTIRQ*/
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}


那么我们注册的这些函数是在什么时候调用的呢,换句话说,中断下部分是在什么时候被执行的呢。

在linux2.4内核中在中断执行函数do_IRQ和系统调用返回前会判断系统当前有没有软中断在申请执行,下面是do_IRQ和arch/i386/kernel/entry.S的代码片段:

if (softirq_active(cpu) & softirq_mask(cpu))
do_softirq();

+++++++++++++++++++++++++++++++++++++++

ENTRY(ret_from_sys_call)
#ifdef CONFIG_SMP
movl processor(%ebx),%eax
shll $CONFIG_X86_L1_CACHE_SHIFT,%eax
movl SYMBOL_NAME(irq_stat)(,%eax),%ecx# softirq_active
testl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx# softirq_mask
#else
movl SYMBOL_NAME(irq_stat),%ecx# softirq_active
testl SYMBOL_NAME(irq_stat)+4,%ecx# softirq_mask
#endif
jne   handle_softirq


handle_softirq:
call SYMBOL_NAME(do_softirq)
jmp ret_from_intr

看到红色标记的函数了吧,对了,就是执行了do_softirq()函数的,代码如下

asmlinkage void do_softirq()
{
int cpu = smp_processor_id();
__u32 active, mask;

if (in_interrupt())
return;

local_bh_disable();

local_irq_disable();
mask = softirq_mask(cpu);
active = softirq_active(cpu) & mask;

if (active) {
struct softirq_action *h;

restart:
/* Reset active bitmask before enabling irqs */
softirq_active(cpu) &= ~active;

local_irq_e