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,则不会唤醒线程,退化为传统中断处理。
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, ¶m),将中断处理线程提升为实时优先级,确保关键中断优先处理。