3. 中断描述符与 irq_desc 结构体:核心数据结构解析
在 Linux 内核中,每一个中断请求(IRQ)都由一个称为 中断描述符 的核心数据结构来管理。这个描述符是内核中断子系统的基石,它串联起硬件控制器、中断处理例程和上层驱动。本章将深入解析 irq_desc、irqaction 和 irq_chip 这三个关键结构体的作用与协作关系。
3.1 irq_desc:中断描述符的“总控室”
irq_desc 是每个 IRQ 线对应的核心管理结构体,内核维护一个全局数组 irq_desc[],数组下标即为 IRQ 号。该结构体包含了中断的状态、处理函数链表、底层芯片操作接口以及统计信息。
关键字段速览:
- irq_data:关联底层硬件控制器(irq_chip)和平台数据。
- action:指向
irqaction链表的头指针,管理所有注册到该 IRQ 的处理函数。 - status_use_accessors:中断状态标志位(如 IRQ_DISABLED、IRQ_INPROGRESS)。
- depth:中断禁用嵌套深度,用于平衡
enable_irq()/disable_irq()调用。 - kstat_irqs:每个 CPU 的中断统计计数,用于性能监控。
| 字段 | 类型 | 作用说明 |
|---|---|---|
irq_data |
struct irq_data |
封装 irq_chip、硬件 IRQ 号、处理器亲和性掩码等底层信息 |
action |
struct irqaction * |
指向中断处理函数链表的头节点,支持共享中断 |
status |
unsigned int |
中断状态标志,如 IRQ_DISABLED、IRQ_WAITING、IRQ_MASKED |
depth |
unsigned int |
记录 disable_irq 的嵌套次数,为 0 时中断才真正使能 |
kstat_irqs |
unsigned int * |
per-CPU 中断计数数组,用于 /proc/interrupts 统计 |
3.2 irqaction:中断处理函数的“挂载点”
每个设备驱动通过 request_irq() 或 request_threaded_irq() 注册中断处理函数时,内核会分配一个 irqaction 结构体,并将其挂入对应 irq_desc 的 action 链表。支持共享中断时,多个 irqaction 会串联成链表,内核依次调用每个处理函数,直到某个函数返回 IRQ_HANDLED。
共享中断的识别: 注册共享中断时,必须设置
IRQF_SHARED 标志。内核在遍历 action 链表时,会调用所有注册的处理函数,由驱动自行通过读取硬件状态寄存器判断是否为自己的设备产生了中断。
| 字段 | 类型 | 作用说明 |
|---|---|---|
handler |
irq_handler_t |
主中断处理函数(上半部),运行在中断上下文 |
thread_fn |
irq_handler_t |
线程化中断处理函数(下半部),运行在进程上下文 |
name |
const char * |
设备名称,显示在 /proc/interrupts 中 |
dev_id |
void * |
设备标识,用于共享中断时区分不同设备,也用于 free_irq() 注销 |
next |
struct irqaction * |
指向下一个 irqaction,形成链表 |
irq |
unsigned int |
该 action 绑定的 IRQ 号 |
// 驱动注册中断的典型流程
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev_id)
{
struct irqaction *action;
// 1. 分配 irqaction 结构体
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
action->handler = handler;
action->name = name;
action->dev_id = dev_id;
action->irq = irq;
// 2. 将 action 挂入 irq_desc[irq]->action 链表
return __setup_irq(irq, action, flags);
}
3.3 irq_chip:硬件中断控制器的“抽象层”
irq_chip 结构体封装了底层中断控制器(如 GIC、IO-APIC、X86 的 PIC)的硬件操作函数。内核通过 irq_chip 提供的回调接口,以统一的方式操作不同架构的中断控制器,实现了硬件无关性。
| 回调函数 | 功能描述 |
|---|---|
irq_mask |
屏蔽(禁用)指定的中断源 |
irq_unmask |
取消屏蔽(使能)指定的中断源 |
irq_ack |
应答中断,通知控制器已接收中断请求 |
irq_set_affinity |
设置中断的 CPU 亲和性,将中断路由到指定 CPU |
irq_set_type |
设置中断触发方式(电平触发/边沿触发) |
irq_startup |
启动中断,通常在首次使能时调用 |
irq_shutdown |
关闭中断,完全禁用中断源 |
注意:
irq_chip 的回调函数运行在中断上下文或持有锁的上下文中,实现时必须保证快速、不可睡眠、不可调用可能导致阻塞的函数(如 kmalloc(GFP_KERNEL))。
3.4 三者协作关系:从硬件中断到驱动处理
当硬件设备触发中断时,内核通过以下流程串联起三个结构体:
- 硬件信号:中断控制器(如 GIC)检测到硬件中断信号。
- irq_chip 回调:内核调用
irq_desc[irq]->irq_data.chip->irq_ack()应答中断,并执行irq_mask()防止嵌套。 - 查找 irq_desc:根据硬件 IRQ 号找到对应的
irq_desc结构体。 - 遍历 action 链表:内核遍历
irq_desc[irq]->action链表,依次调用每个irqaction->handler()。 - 处理与统计:处理函数执行完毕后,更新
kstat_irqs统计计数,最后调用irq_unmask()重新使能中断。
3.5 数据结构关系总结
核心要点:
- irq_desc:每个 IRQ 一个,是中断管理的总入口,包含状态、统计和链表头。
- irqaction:每个设备驱动注册一个,挂载到 irq_desc 的 action 链表上,支持共享中断。
- irq_chip:每个中断控制器一个,封装了 mask/unmask/ack/set_affinity 等硬件操作。
- 协作流程:硬件中断 → irq_chip 应答 → irq_desc 查找 → irqaction 链表遍历 → 驱动处理函数执行。