对于已经线程化的情况,调用 wake_up_process() 函数唤醒中断处理线程,并开始运行,内核线程将调用 do_hardirq() 来处理相应的中断,该函数将判断是否有中断需要被处理,如果有就调用 handle_IRQ_event() 来处理。handle_IRQ_event() 将直接调用相应的中断处理函数来完成中断处理。
不难看出,不管是线程化还是非线程化的中断,最终都会执行 handle_IRQ_event() 函数来调用相应的中断处理函数,只是线程化的中断处理函数是在内核线程中执行的。
并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。如果某个中断需要被实时处理,它可以像时钟中断那样,用 SA_NODELAY 标志来声明自己非线程化,例如:
static struct irqaction irq0 = {
timer_interrupt, SA_INTERRUPT | SA_NODELAY, CPU_MASK_NONE, "timer", NULL, NULL
};
其中,SA_NODELAY 到 IRQ_NODELAY 之间的转换,是在 setup_irq() 函数中完成的。
中断负载均衡—SMP体系结构下的中断
中断负载均衡的实现主要封装在 arch\ arch\i386\kernel\io-apic.c 文件中。如果在编译内核时配置了 CONFIG_IRQBALANCE 选项,那么 SMP 体系结构中的中断负载均衡将以模块的形式存在于内核中。
late_initcall(balanced_irq_init);
#define late_initcall(fn)module_init(fn) //include\linux\init.h
在 balanced_irq_init() 函数中,将创建一个内核线程来负责中断负载均衡:
static int __init balanced_irq_init(void)
{ ……
printk(KERN_INFO "Starting balanced_irq\n");
if (kernel_thread(balanced_irq, NULL, CLONE_KERNEL) >= 0)
return 0;
else
printk(KERN_ERR "balanced_irq_init: failed to spawn balanced_irq");
……
}
在 balanced_irq() 函数中,每隔 5HZ=5s 的时间,将调用一次 do_irq_balance() 函数,进行中断的迁徙。将重负载 CPU 上的中断迁移到较空闲的CPU上进行处理。
总结
随着中断亲和力和中断线程化的相继实现,Linux 内核在 SMP 和实时性能方面的表现越来越让人满意,完全有理由相信,在不久的将来,中断线程化将被合并到基线版本中。本文对中断线程化的分析只是起一个抛砖引玉的作用,当新特性发布时,不至于让人感到迷茫。
注释 1:轮询也不是毫无用处,比如NAPI,就是轮询与中断相结合的经典案例。
苏 春艳, 在读研究生
杨 小华 (normalnotebook@126.com), 在读研究生