3、Linux中断子系统架构:中断描述符、irq_chip、中断处理函数注册机制
Linux中断子系统是内核中负责管理硬件中断请求的核心模块,其设计遵循分层与抽象原则,使得上层驱动开发者无需关心底层中断控制器的具体实现细节。理解中断描述符(irq_desc)、中断控制器抽象(irq_chip)以及中断处理函数的注册机制,是掌握中断亲和性与共享中断故障排查的基础。
3.1 中断描述符(irq_desc)——中断的“身份证”
每个中断源在内核中对应一个irq_desc结构体实例,该结构体集中管理该中断的所有状态与回调信息。系统启动时,内核根据平台支持的最大中断数(NR_IRQS)预分配一个irq_desc数组。
核心字段说明:
- irq_data:包含
irq_chip指针、硬件中断号、中断控制器私有数据。 - action:指向
irqaction链表的头指针,一个中断可挂载多个处理函数(共享中断)。 - status_use_accessors:中断状态标志位,如
IRQ_DISABLED、IRQ_INPROGRESS。 - depth:中断禁用深度计数,用于嵌套禁用/启用场景。
- lock:自旋锁,保护该描述符的并发访问。
3.2 irq_chip——中断控制器的软件抽象
irq_chip结构体是内核为不同中断控制器(如APIC、GIC、IO-APIC)提供的统一接口层。每个irq_chip实例封装了操作底层中断控制器的回调函数指针,使得中断子系统核心代码可以以多态方式操作硬件。
| 回调函数 | 功能描述 | 典型实现场景 |
|---|---|---|
irq_mask |
屏蔽中断源(禁止传递) | 写中断控制器屏蔽寄存器 |
irq_unmask |
解除屏蔽中断源 | 写中断控制器解除屏蔽寄存器 |
irq_ack |
应答中断(清除挂起状态) | 写EOI(End of Interrupt)寄存器 |
irq_set_affinity |
设置中断亲和性(CPU掩码) | 写中断重定向表项(如MSI-X Table) |
irq_set_type |
配置中断触发方式(电平/边沿) | 配置中断控制器触发寄存器 |
排查要点:当发现中断无法正确路由到目标CPU时,应首先检查
irq_chip中的irq_set_affinity回调是否被正确实现。部分老旧或模拟中断控制器可能不支持亲和性设置,导致/proc/irq/N/smp_affinity写入无效。
3.3 中断处理函数注册机制
设备驱动通过request_irq()或request_threaded_irq()向内核注册中断处理函数。注册的核心操作是将irqaction结构体挂载到对应irq_desc的action链表上。
3.3.1 注册流程关键步骤
- 参数校验:检查中断号有效性、处理函数指针非空、标志位合法。
- 分配irqaction:内核分配并初始化
irqaction结构体,填充处理函数指针、设备ID、中断名称、标志位(IRQF_SHARED等)。 - 获取irq_desc锁:调用
__setup_irq()内部函数,持有irq_desc->lock自旋锁。 - 检查共享兼容性:如果中断已被注册,新注册必须设置
IRQF_SHARED标志,且触发方式必须一致。 - 挂载到链表:将新
irqaction插入action链表尾部。 - 配置中断控制器:如果这是该中断的第一个处理函数,调用
irq_chip的irq_startup或irq_unmask启用中断。
共享中断注册陷阱:所有共享同一中断号的驱动必须使用
IRQF_SHARED标志,且中断触发方式(电平/边沿)必须完全一致。边沿触发的中断在共享场景下极易丢失中断,推荐共享中断使用电平触发方式。
/* 典型的中断注册示例 */
int devm_request_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, unsigned long irqflags,
const char *devname, void *dev_id)
{
/* 1. 参数校验 */
if (!handler || irq < 0)
return -EINVAL;
/* 2. 分配并初始化 irqaction */
struct irqaction *action = kzalloc(sizeof(*action), GFP_KERNEL);
action->handler = handler;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
/* 3. 调用内部注册函数 */
return __setup_irq(irq, action, IRQF_COND_ONESHOT);
}
3.3.2 中断处理函数执行路径
当中断触发时,内核通过irq_desc->handle_irq进入通用中断处理层,然后遍历action链表,依次调用每个注册的处理函数。对于共享中断,内核会逐个调用直到某个处理函数返回IRQ_HANDLED或遍历完整个链表。
3.4 中断描述符与irq_chip的关联
irq_desc通过内嵌的irq_data结构体与irq_chip建立关联。irq_data包含chip指针指向具体的irq_chip实例,以及chip_data指针指向中断控制器的私有数据。这种设计使得中断子系统核心代码可以统一调用irq_chip的回调,而无需关心底层硬件差异。
排查关联性故障:
- 如果
/proc/interrupts中某中断的“chip”列显示为IO-APIC或GIC-v3等名称,说明irq_chip已正确关联。 - 若显示
None或unknown,通常表示中断控制器驱动未正确初始化,或设备树/ACPI表中缺少中断控制器节点。 - 亲和性设置失败时,检查
irq_chip->irq_set_affinity是否为NULL,若为NULL则内核会返回-EINVAL。