日期:2014-05-16 浏览次数:20804 次
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