Linux 中断注册机制详解
Linux 操作系统的中断管理是内核的核心功能之一,它允许硬件设备通过中断信号通知 CPU 处理异步事件,中断注册作为中断处理流程的首要步骤,涉及驱动程序与内核中断子系统的交互,本文将深入探讨 Linux 中断注册的基本概念、实现流程、关键函数及注意事项,帮助开发者理解如何正确、高效地注册中断处理程序。

中断的基本概念
中断是硬件向 CPU 发送的请求信号,用于暂停当前任务并转而处理紧急事件,在 Linux 中,中断分为硬中断(由硬件触发)和软中断(由软件触发),硬中断进一步分为可屏蔽中断和不可屏蔽中断,其中可屏蔽中断是最常见的类型,由设备驱动程序管理,中断处理程序(Interrupt Service Routine, ISR)是内核中响应中断的函数,其执行效率直接影响系统性能。
中断注册的本质是将设备的中断号与对应的处理程序关联起来,并配置中断控制器(如 Intel 的 APIC 或 ARM 的 GIC),内核通过中断描述符表(Interrupt Descriptor Table, IDT)管理中断向量,而 Linux 则在此基础上抽象出中断子系统,为驱动程序提供统一的接口。
中断注册的核心函数
Linux 内核提供了 request_irq() 函数用于注册中断处理程序,其原型如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
- irq:要注册的中断号,通常通过设备树或平台资源获取。
- handler:中断处理函数指针,其原型为
irqreturn_t (*)(int, void *)。 - flags:中断标志位,如
IRQF_SHARED(共享中断)、IRQF_TRIGGER_RISING(上升沿触发)等。 - name:中断名称,用于
/proc/interrupts等工具显示。 - dev:设备标识符,用于共享中断时的区分,通常为设备结构体指针。
与 request_irq() 对应的是 free_irq(),用于注销中断处理程序,共享中断时,需确保所有处理程序均被注销后才会释放中断资源。
中断注册的流程
中断注册通常遵循以下步骤:
-
获取中断号
中断号可通过设备树(Device Tree)、平台资源(Platform Resource)或 PCI 配置空间获取,在设备树中,中断属性通常以interrupts定义,内核会解析并分配唯一的中断号。 -
定义中断处理程序
中断处理函数需满足以下要求:
- 执行速度尽可能快,避免耗时操作(如睡眠)。
- 使用
irqreturn_t返回类型,明确处理结果(如IRQ_HANDLED或IRQ_NONE)。 - 对于共享中断,需检查是否为当前设备触发中断。
-
配置中断标志
中断标志需根据硬件特性设置,按键设备通常使用IRQF_TRIGGER_FALLING(下降沿触发),而网卡可能使用IRQF_SHARED和IRQF_DISABLED(禁用本地 CPU 中断)。 -
注册中断
调用request_irq()注册中断,并检查返回值(成功返回 0,失败返回错误码)。ret = request_irq(irq, my_interrupt_handler, IRQF_SHARED, "my_device", dev); if (ret) { pr_err("Failed to request IRQ: %d\n", ret); return ret; } -
资源清理
在驱动卸载时,需调用free_irq()释放中断资源,避免内存泄漏。free_irq(irq, dev);
中断共享与线程化
Linux 支持多个设备共享同一个中断线,但需满足以下条件:
- 所有设备的中断标志兼容(如触发方式一致)。
- 处理程序通过
dev参数区分设备来源。 - 使用
IRQF_SHARED标志位。
对于复杂的中断处理(如需要等待硬件响应),Linux 提供了中断线程化机制,通过 IRQF_ONESHOT 标志位,可将中断处理程序放到内核线程中执行,允许睡眠操作。
request_irq(irq, threaded_handler, IRQF_ONESHOT, "my_device", dev);
中断注册的注意事项
-
中断处理程序的约束
- 避免在 ISR 中调用可能睡眠的函数(如
kmalloc(GFP_KERNEL))。 - 使用
spin_lock_irqsave()保护共享数据,避免死锁。
- 避免在 ISR 中调用可能睡眠的函数(如
-
中断号的唯一性
确保中断号未被其他设备占用,可通过/proc/interrupts查看当前中断分配情况。
-
错误处理
注册失败时需回滚资源(如释放已申请的 I/O 端口或内存)。 -
性能优化
对于高频中断(如网卡),尽量减少 ISR 中的逻辑,将任务延迟到软中断或工作队列中处理。
实例:字符设备驱动中断注册
以下是一个简单的字符设备驱动中断注册示例:
#include <linux/interrupt.h>
#include <linux/module.h>
static irqreturn_t my_interrupt_handler(int irq, void *dev_id) {
struct my_device *dev = dev_id;
// 处理中断逻辑
return IRQ_HANDLED;
}
static int __init my_driver_init(void) {
int irq;
struct my_device *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
irq = platform_get_irq(pdev, 0); // 从平台设备获取中断号
if (irq < 0) {
pr_err("Failed to get IRQ\n");
return irq;
}
if (request_irq(irq, my_interrupt_handler, IRQF_SHARED,
"my_char_device", dev)) {
pr_err("Failed to register IRQ\n");
return -EIO;
}
return 0;
}
static void __exit my_driver_exit(void) {
free_irq(irq, dev);
kfree(dev);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
Linux 中断注册是驱动开发的基础环节,涉及中断号获取、处理程序定义、标志配置等多个步骤,正确理解和使用 request_irq()、free_irq() 等函数,遵循中断处理的约束和最佳实践,对于构建稳定高效的驱动程序至关重要,通过合理的中断管理,可以充分发挥硬件性能,确保 Linux 系统的实时性和可靠性。
















