3. 中断线程化API:request_threaded_irq()函数详解与参数含义

在Linux实时化改造中,request_threaded_irq()是实现中断线程化的核心API。它将传统中断处理程序拆分为上半部(hardirq handler)和下半部(thread handler),使下半部在可睡眠的进程上下文中运行,从而降低中断延迟并支持实时调度策略。

核心概念: 中断线程化后,中断处理线程可以被SCHED_FIFO或SCHED_RR实时调度器管理,避免硬中断上下文对系统实时性的冲击。

3.1 函数原型与头文件

#include <linux/interrupt.h>

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);

3.2 参数详解

参数 类型 含义与说明
irq unsigned int 中断号,由platform_get_irq()或pci_irq_vector()获取
handler irq_handler_t 上半部处理函数(hardirq),在硬中断上下文中执行,返回IRQ_WAKE_THREAD唤醒线程
thread_fn irq_handler_t 下半部线程处理函数,在可睡眠的进程上下文中运行,返回IRQ_HANDLED
irqflags unsigned long 中断标志位,如IRQF_TRIGGER_RISING、IRQF_SHARED等
devname const char * 设备名称,用于/proc/interrupts标识
dev_id void * 设备ID指针,共享中断时用于区分设备,通常传入设备结构体指针

3.3 返回值与错误码

  • 0:注册成功
  • -EINVAL:中断号无效或参数错误
  • -EBUSY:中断已被占用且未设置IRQF_SHARED
  • -ENOMEM:内存分配失败

3.4 上半部与下半部的协作流程

关键机制: 上半部必须返回IRQ_WAKE_THREAD才能触发线程化处理。如果返回IRQ_HANDLED,则不会唤醒线程,退化为传统中断处理。
request_threaded_irq() 中断处理流程 硬件中断触发 上半部 handler() 硬中断上下文,快速处理 返回 IRQ_WAKE_THREAD? 是 下半部 thread_fn() 进程上下文,可睡眠 处理完成 否 退化为传统中断

3.5 典型使用模式

在驱动开发中,request_threaded_irq()通常与设备初始化流程配合使用。以下是一个典型的注册示例:

static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
    struct my_device *dev = (struct my_device *)dev_id;

    /* 快速处理:清除硬件中断标志,保存关键状态 */
    writel(0x01, dev->regs + INT_CLR);

    /* 唤醒线程处理剩余工作 */
    return IRQ_WAKE_THREAD;
}

static irqreturn_t my_irq_thread(int irq, void *dev_id)
{
    struct my_device *dev = (struct my_device *)dev_id;

    /* 可睡眠操作:数据拷贝、唤醒等待队列、调用 completion */
    dev->data_ready = 1;
    wake_up_interruptible(&dev->wq);

    return IRQ_HANDLED;
}

static int my_probe(struct platform_device *pdev)
{
    struct my_device *dev;
    int irq, ret;

    /* ... 分配设备结构体、映射寄存器 ... */

    irq = platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;

    ret = request_threaded_irq(irq, my_irq_handler,
                               my_irq_thread,
                               IRQF_TRIGGER_RISING | IRQF_SHARED,
                               "my_device", dev);
    if (ret) {
        dev_err(&pdev->dev, "failed to request threaded irq %d\n", ret);
        return ret;
    }

    return 0;
}

3.6 参数配置注意事项

常见陷阱:
  • 如果handler返回IRQ_HANDLED而非IRQ_WAKE_THREAD,线程永远不会被唤醒
  • 共享中断(IRQF_SHARED)必须提供非NULL的dev_id,否则内核无法区分设备
  • 线程函数中不能调用任何在硬中断上下文中禁止的API(如spin_lock_irqsave)

3.7 与传统request_irq()的对比

特性 request_irq() request_threaded_irq()
处理上下文 全部在硬中断上下文 上半部硬中断 + 下半部进程上下文
可睡眠能力 不允许睡眠 下半部允许睡眠
实时调度 不支持 线程可设置SCHED_FIFO优先级
中断延迟 处理期间关闭本地中断 上半部极短,延迟可控
兼容性 所有内核版本 Linux 2.6.30+

3.8 实时化场景下的参数选择建议

  • irqflags:优先使用IRQF_NO_THREAD?不——实时化应避免此标志,确保中断可被线程化
  • dev_id:传入包含实时相关字段(如优先级、周期)的设备结构体
  • thread_fn:内部可调用sched_setscheduler()提升线程优先级
实时优化技巧: 在thread_fn入口处调用sched_setscheduler_nocheck(current, SCHED_FIFO, &param),将中断处理线程提升为实时优先级,确保关键中断优先处理。