The only difference between an interrupt gate and a trap gate is the way the
processor handles the IF flag in the EFLAGS register. When accessing an exception-
or interrupt-handling procedure through an interrupt gate, the processor clears the
IF flag to prevent other interrupts from interfering with the current interrupt handler.
A subsequent IRET instruction restores the IF flag to its value in the saved contents of EFFLAGS register on the stack. Accessing a handler through a trap gate does not affect the IF flag.
这段话说明了在通过interrupt gate去调用中断处理时,CPU会自动清除IF 标志,当中断处理例程结束并返还时,通过IRET指令又将IF标志恢复。而EFLAGS的IF标志使用设置该CPU中断是否enable的标志。看到这里大家可能会有一些奇怪,按照intel的说明,岂不是说intel的CPU不支持中断嵌套。我来说一下我的理解,对于中断的定义,Intel定义的中断与咱们平时说的中断不大一样。咱们平时说的中断,应该说是Intel定义的中断的一种。而trap也被Intel认为是一种中断。这么说吧,Intel眼中的中断就是只要打断了正常的处理流程,都可以称为中断——这是我下的定义,不太准确,但大概意思是这样。所以exception和系统trap都被看完中断。而通过trap gate去调用处理函数时,CPU是不会自动禁掉中断的,这时如果发生高优先级的中断,即发生了中断嵌套。
在Linux kernel中,中断处理使用的时interrupt gate,所以从平台上看,当Linux运行在x86平台时,中断就已经不支持嵌套了——当然,要是中断函数里面非要把中断再次打开,也没办法,此处不考虑这种情况。
现在看Linux kernel本身的代码是否支持中断嵌套。下面查看do_IRQ的实现,因为这个函数是依赖于平台实现的。首先查看x86平台的实现
unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
/* high bit used in ret_from_ code */
unsigned vector = ~regs->orig_ax;
unsigned irq;
exit_idle();
irq_enter();
irq = __this_cpu_read(vector_irq[vector]);
if (!handle_irq(irq, regs)) {
ack_APIC_irq();
if (printk_ratelimit())
pr_emerg("%s: %d.%d No irq handler for vector (irq %d)\n",
__func__, smp_processor_id(), vector, irq);
}
irq_exit();
set_irq_regs(old_regs);
return 1;
}
这部分代码没有明显处理中断开关的代码,下面看其调用的handle_irq
bool handle_irq(unsigned irq, struct pt_regs *regs)
{
struct irq_desc *desc;
int overflow;
overflow = check_stack_overflow();
/* 得到中断描述符 */
desc = irq_to_desc(irq);
if (unlikely(!desc))
return false;
if (!execute_on_irq_stack(overflow, desc, irq)) {
if (unlikely(overflow))
print_stack_overflow();
/* 调用中断处理函数 */
desc->handle_irq(irq, desc);
}
return true;
}
从上面的代码中,可以看出没有直接去对于中断使能位的任何处理。那么在x86平台上,完全依赖于x86的处理。这样的话,可以得出结论,Linux内核在x86平台上不支持中断嵌套。——当然,如果非得在中断处理函数中,enable中断标志,来允许中断嵌套。
我认为这种行为不是好的方法,因为x86既然本身限制了中断嵌套,那么必然有它的限制。如果人为的破坏这个设计,肯定会引发错误。
最后,在引用一段Intel手册中的一段话。
Because IA-32 architecture tasks are not re-entrant, an interrupt-handler task must disable interrupts between the time it completes handling the interrupt and the time it executes the IRET instruction. This action prevents another interrupt from occurring while the interrupt task’s TSS is still marked busy, which would cause a general-protection (#GP) exception.
这里明确说明了Intel的IA-32的架构,不支持中断嵌套(这里指的是可屏蔽中断)。
最后,感谢一下amarant同学,他指出了我最早引用的handler_irq的代码不正确。我之前使用cscope跳到handler_irq的定义时,没有注意到是alpha平台的定义。现已改正博文。