Linux支持中断嵌套吗?
曾经是支持的,现在已经不支持了!
在早期的Linux内核版本中,支持中断嵌套,即在一个中断服务程序(Interrupt Service Routine,ISR)中可以响应其他中断。当时,Linux内核将中断分为两种类型:快中断和慢中断。快中断申请时带IRQF_DISABLED标志,在ISR中不允许新的中断进来;慢中断申请时不带IRQF_DISABLED标志,在ISR中允许新的其他中断嵌套进来。
快中断的申请示例如下:
ret = request_irq(host->irq, acornscsi_intr, IRQF_DISABLED, "acornscsi", ashost);
其背后原理是,内核在响应硬件中断的过程中,会根据 IRQF_DISABLED 标志做不同的处理,如果没有定义 IRQF_DISABLED 标志,那么就会在调用中断服务程序前重新开启中断,在这种情况下,中断服务程序在执行过程中就可能被其他中断打断,造成中断嵌套。
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
...
/* 如果没有定义 IRQF_DISABLED,那么就在调用中断服务程序前,开启中断 */
if (!(action->flags & IRQF_DISABLED))
local_irq_enable_in_hardirq();
/* 调用中断服务程序 */
do {
ret = action->handler(irq, action->dev_id);
if (ret == IRQ_HANDLED)
status |= action->flags;
retval |= ret;
action = action->next;
} while (action);
...}
但是,从如下2010年的一笔提交开始,已经去掉了这个机制。原因就是考虑到中断嵌套会带来栈溢出等风险。
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e58aa3d2d0cc
其具体的修改就是去掉了对 IRQF_DISABLED 标志的判断,无论是否设置这个标志,内核都不会在处理中断的过程中开启中断。
由于Linux中断处理过程分成上半部和下半部,我们这里说的中断服务程序(ISR)指的是上半部。为了避免误解,更明确的表述是,内核不会在中断上半部处理完之前开启中断。当退出中断上半部的处理程序后,中断会被开启,所以下半部是在开中断的情况下执行的。
另外注意下线程化中断。
传统的中断申请函数是下面这样的,其参数 handler 表示中断服务函数。
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
而线程化中断申请函数是这样的,其参数不仅有 handler ,还有 thread_fn 。handler 的意义与传统函数一样,thread_fn 由内核线程执行。
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)
线程化中断,内核提供了一个标志来控制中断是否延迟开启。如果申请中断时设置了IRQF_ONESHOT标志,则Linux中断系统会保证在 thread_fn 执行完后才开中断,否则就和传统处理过程一样,在 handler 执行完后就开中断。如果想进一步了解线程化中断,那么可以参考这篇文章 中断线程化与强制中断线程化 。