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

Linux Interrupt Nesting

其具体的修改就是去掉了对 IRQF_DISABLED 标志的判断,无论是否设置这个标志,内核都不会在处理中断的过程中开启中断。

Linux Interrupt Nesting

由于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 执行完后就开中断。如果想进一步了解线程化中断,那么可以参考这篇文章 中断线程化与强制中断线程化 。

Linux Interrupt Nesting