gic 中断触发类型
一、触发类型
1. 边沿触发(Edge-triggered)
-
触发条件:中断信号从低到高(或高到低)的电平跳变瞬间触发。
-
硬件要求:
-
中断信号必须为脉冲形式(短时有效)。
-
GIC的
GICD_ICFGR
寄存器配置对应中断为边沿类型(IRQ_TYPE_EDGE_*
)。
-
-
Linux配置:
设备树中标记为IRQ_TYPE_EDGE_RISING
(上升沿)或IRQ_TYPE_EDGE_FALLING
(下降沿)。 -
优点:
避免电平持续有效导致的重复触发,适用于按键、时钟等短脉冲信号。 -
缺点:
信号丢失风险:若两次跳变间隔小于CPU响应时间,GIC可能仅记录一次中断(需硬件防抖)。
2. 电平触发(Level-triggered)
-
触发条件:中断信号在持续保持有效电平(高/低)期间触发。
-
硬件要求:
-
中断源需维持电平直到CPU处理完成(如外设DMA传输中)。
-
GIC的
GICD_ICFGR
寄存器配置为电平类型(IRQ_TYPE_LEVEL_*
)。
-
-
Linux配置:
设备树标记为IRQ_TYPE_LEVEL_HIGH
(高电平)或IRQ_TYPE_LEVEL_LOW
(低电平)。 -
优点:
无信号丢失,适用于共享中断线(多个设备共用一根中断线)。 -
缺点:
临界区要求:-
CPU必须在中断处理中清除外设中断状态,否则GIC会持续重发中断。
-
Linux需在中断处理末尾调用
chained_irq_exit()
同步状态。
-
3. 消息触发(Message-Based Interrupts, MSI/MSI-X)
-
触发原理:
通过内存写入替代物理信号线,CPU收到特定内存消息后触发中断。 -
硬件要求:
-
支持PCIe的设备(如GPU、网卡)通过写
ITS(Interrupt Translation Service)
寄存器发起中断。 -
GICv3+的ITS模块将内存消息映射为虚拟LPI中断。
-
-
Linux实现:
// 驱动调用pci_alloc_irq_vectors()申请MSI中断 pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
-
优点:
-
无物理线约束,支持数千个中断号。
-
降低共享中断冲突(每个设备独占中断向量)。
-
-
缺点:
依赖PCIe总线协议和ITS硬件支持。
4. 特定事件触发(如WFE/WFI事件)
-
严格意义不属于中断,但属于ARMv8事件响应机制:
-
WFI(Wait For Interrupt):暂停CPU直到任意中断到来。
-
WFE(Wait For Event):暂停CPU直到
SEV
指令或事件信号(包括中断)。
-
-
使用场景:
Linux空闲循环(cpu_idle_loop()
)中通过WFI降低功耗:// arch/arm64/kernel/process.c static void do_idle(void) {while (1) {if (need_resched()) schedule();elsewfi(); // 进入低功耗状态等待中断} }
二、电平触发和边沿触发中断生命周期
三、电平触发和边沿触区别
特性 | 边沿触发(Edge) | 电平触发(Level) |
---|---|---|
信号要求 | 短脉冲(跳变即失效) | 持续电平(直至软件清除) |
Pending→Active转换 | 仅靠读ICC_IAR1_EL1 触发 | 读ICC_IAR1_EL1 或 电平持续有效 |
结束中断的关键动作 | 写ICC_EOIR1_EL1 | 写ICC_EOIR1_EL1 + 清除外设电平 |
状态机自主性 | 高(信号瞬态,不依赖外设持续行为) | 低(依赖外设维持/撤销电平) |
四、中断是否需要disable_irq准则
1、根本性决策条件
条件 | 需禁用中断(disable_irq) | 无需禁用中断 |
---|---|---|
硬件操作是否原子化 | 非原子操作(如多步寄存器访问) | 原子操作(如单寄存器读写) |
系统中断嵌套支持 | 允许嵌套且可能破坏关键段 | 禁止嵌套或关键段已受其他保护 |
本质矛盾
-
中断嵌套:高优先级中断抢占低优先级中断 → 可能导致关键操作被分割。
-
硬件原子性:某些外设要求操作序列不可分割(如先写地址寄存器再读数据寄存器)。
2、硬件中断可被打扰的典型场景
a: 必须禁用中断的情况
// 案例:I2C 控制器操作(非原子)
static void i2c_read_reg(struct device *dev, u8 reg) {disable_irq(dev->irq); // 防止中断打断I2C时序i2c_write(dev, REG_ADDR, reg); // 写地址寄存器data = i2c_read(dev); // 读数据寄存器enable_irq(dev->irq); // 重新允许中断
}
硬件要求:
I2C协议要求地址+数据
的连续性,若中途被中断,可能导致总线超时或从设备状态机错误。
b. 无需禁用中断的情况
// 案例:原子计数器递增
static irqreturn_t timer_handler(int irq, void *dev) {atomic_inc(&dev->count); // 原子操作,无需保护return IRQ_HANDLED;
}
3、中断嵌套的系统支持差异
a. 允许嵌套的架构(如ARMv7)
-
风险:若未禁用中断,高优先级中断可能抢占正在处理的中断。
-
保护措施:
// ARMv7 中断嵌套保护 void irq_handler() {if (in_interrupt()) { // 检查是否已在中断上下文disable_irq_nosync(irq); // 禁止当前中断线handle_nested_irq(irq); // 特殊处理嵌套中断} }
b. 禁止嵌套的架构(如ARMv8非安全态)
-
优势:默认不嵌套,减少
disable_irq
的使用需求。 -
例外:若手动启用嵌套(如通过
ICC_CTLR_EL3
),需显式保护。
五、结论
维度 | 边沿触发 | 电平触发 |
---|---|---|
本质 | 事件驱动(Event-Centric) | 状态驱动(State-Centric) |
黄金法则 | 确保信号跳变间隔 > 中断处理时间 | 中断处理中必须清除外设状态 |
适用场景 | 时钟、按键(防抖后)、非共享设备 | DMA、共享中断设备、硬件错误信号 |
Linux API示例 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_LOW |
杀手级风险 | 高频信号丢失 | 中断风暴 |
-
电平触发是“粘性的”——像胶水一样粘住CPU直到处理完成,但操作不当会淹没系统。
-
边沿触发是“瞬态的”——像闪电一样稍纵即逝,但可能被后续信号覆盖。
怎么选择取决于外设行为,而清除外设状态是电平触发的生死线!