一、中断号
中断号是计算机用来区分不同硬件信号的唯一编号。当键盘输入、定时器到期或硬盘完成任务等事件发生时,硬件会通过中断号通知 CPU,让 CPU 暂停当前工作,转去处理这些紧急事件。
在 Linux 系统中,中断号通常用整数(int 类型)表示。这个整数能覆盖从简单设备到复杂系统的中断需求。比如早期计算机最多支持 15 个中断,而现代系统可支持 256 个甚至更多,用整数存储完全足够。
当硬件触发中断时,Linux 内核会根据中断号找到对应的处理程序。系统预先将每个中断号与处理代码绑定,形成类似"电话簿"的中断表(IDT)。这样 CPU 就能快速定位到正确的处理流程,确保不同设备的中断不会混淆。
总结
中断号就像硬件事件的"身份编号",是连接硬件信号与软件处理的关键纽带。Linux 用整数类型管理中断号,既简单直接又能满足各种需求,保证系统稳定运行。
二、request_irq 函数
在 Linux 内核中要想使用某个中断是需要申请的,request_irq 函数用于申请中断。request_irq 函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用。request_irq 函数会激活(使能)中断,所以不需要我们手动去使能中断。函数原型如下:
int request_irq(unsigned int irq,
irq_handler_t handler,
unsigned long flags,
const char *name,
void *dev);2
3
4
5
1、参数解析
irq(中断号)
irq 是用于标识特定中断的编号,它对应于硬件中断控制器中的某一条中断线。每个中断号都唯一地映射到某个硬件设备或事件上。通过指定 irq,系统能够准确定位并响应特定的硬件中断请求。
handler(中断处理函数)
当中断发生时,内核会调用由 handler 指定的中断处理函数。这个函数是驱动程序的核心逻辑所在,负责对中断事件进行快速响应和处理。设计良好的中断处理函数应尽可能简洁高效,以减少对系统性能的影响。
flags(中断标志)
flags 是一组控制中断行为的标志位,定义了中断的触发条件、共享模式以及其他特性。这些标志可以在内核头文件 include/linux/interrupt.h 中找到完整的定义。通过选择合适的标志,开发者可以精确控制中断的行为,从而满足不同场景的需求。
name(中断名称)
name 是为中断赋予的一个可读性较高的字符串标识符。设置后,该名称会出现在 /proc/interrupts 文件中,便于调试和监控。
dev(设备标识)
当 flags 设置为 IRQF_SHARED 时,多个设备可以共享同一条中断线。此时,dev 参数成为区分这些设备的关键标识。通常,dev 被设置为指向设备结构体的指针,并在中断处理函数中作为第二个参数传递,以便识别具体的设备来源。
2、返回值
request_irq 函数的返回值用于指示中断申请的结果:
- 0 表示中断申请成功;
- 负值 表示失败,其中
-EBUSY特别指出目标中断已被其他设备占用。
3、常用中断标志详解
| 标志 | 描述 |
|---|---|
IRQF_SHARED | 多个设备共享同一中断线时使用 |
IRQF_ONESHOT | 中断处理后不再自动重新启用 |
IRQF_TRIGGER_NONE | 不指定任何触发条件 |
IRQF_TRIGGER_RISING | 上升沿触发 |
IRQF_TRIGGER_FALLING | 下降沿触发 |
IRQF_TRIGGER_HIGH | 高电平触发 |
IRQF_TRIGGER_LOW | 低电平触发 |
三、free_irq 函数
使用中断的时候需要通过 request_irq 函数申请,使用完成以后就要通过 free_irq 函数释放掉相应的中断。如果中断不是共享的,那么 free_irq 会删除中断处理函数并且禁止中断。free_irq 函数原型如下所示:
void free_irq(unsigned int irq, void *dev_id);函数参数与返回值的含义:
- irq:此参数代表需要释放的中断资源。
- dev_id:当中断被配置为共享模式(
IRQF_SHARED)时,此参数扮演了至关重要的角色。它作为一种标识符,用于区分共享同一中断线的不同设备或处理函数。值得注意的是,只有在释放最后一个与该中断相关的处理函数时,中断才会被真正禁用。 - 返回值:该函数不返回任何值。
四、中断处理函数(上半部驱动)
在使用 request_irq 函数申请中断时,需要为中断处理程序指定一个回调函数。该回调函数的原型如下所示:
irqreturn_t (*irq_handler_t)(int, void *);其中,第一个参数是触发中断的中断号;第二个参数是一个通用指针(void *),通常与 request_irq 函数中的 dev_id 参数保持一致。
中断处理函数的返回值类型为 irqreturn_t,它是一个枚举类型,定义如下:
enum irqreturn {
IRQ_NONE = (0 << 0), // 中断未被处理
IRQ_HANDLED = (1 << 0), // 中断已成功处理
IRQ_WAKE_THREAD = (1 << 1), // 唤醒线程化中断处理
};
typedef enum irqreturn irqreturn_t;2
3
4
5
6
7
三种返回值
IRQ_NONE:表示当前中断并未被处理。这种情况通常发生在共享中断线的场景中。IRQ_HANDLED:表示中断已被成功处理。这是大多数中断处理函数的标准返回值。IRQ_WAKE_THREAD:用于指示内核唤醒线程化的中断处理程序。这种机制常用于需要复杂处理逻辑的场景。
在实际编程中,中断处理函数的返回值通常采用以下形式:
return IRQ_RETVAL(IRQ_HANDLED);五、中断使能与禁止函数
1、基础控制函数
void enable_irq(unsigned int irq);
void disable_irq(unsigned int irq);2
这两个函数分别用于使能和禁止指定的中断源。disable_irq 函数在执行时会等待当前正在处理的中断服务程序完成后再返回。
2、不等待版本
void disable_irq_nosync(unsigned int irq);与 disable_irq 不同,disable_irq_nosync 在调用后会立即返回,而无需等待当前中断处理程序的执行完成。
3、全局中断控制
local_irq_enable();
local_irq_disable();2
local_irq_enable 用于重新开启当前处理器的全局中断,而 local_irq_disable 则用于关闭全局中断。
注意
直接调用 local_irq_disable/local_irq_enable 在多任务环境中可能引发问题。例如:任务 A 关闭中断后,任务 B 重新打开中断会导致任务 A 失去保护。推荐使用下面的"保存/恢复"版本。
4、保存与恢复中断状态
local_irq_save(flags);
local_irq_restore(flags);2
local_irq_save 不仅会关闭全局中断,还会将当前的中断状态保存到 flags 中。local_irq_restore 则负责根据 flags 的值将中断状态还原到调用 local_irq_save 时的状态。这种机制确保了每个任务在操作全局中断时都能够"和平共处"。