Linux handle_level_irq电平触发与mask_ack_irq序列
handle_level_irq是Linux内核为电平触发中断提供的标准处理函数。电平触发中断的特点是:当中断信号线处于有效电平(高或低)时,中断请求持续有效。与边沿触发不同,电平触发不会锁存脉冲信号,因此在处理函数返回前必须确保信号线上的有效电平已被设备清除,否则中断会立即再次触发。handle_level_irq围绕mask_ack_irq序列管理这一行为。
函数实现如下:
```c
void handle_level_irq(struct irq_desc *desc)
{
raw_spin_lock(&desc->lock);
mask_ack_irq(desc);
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
if (unlikely(irqd_irq_disabled(&desc->irq_data))) {
desc->istate |= IRQS_PENDING;
goto out_unlock;
}
if (unlikely(!desc->action)) {
goto out_unlock;
}
kstat_incr_irqs_this_cpu(desc);
irq_compat_set_prog_affinity(desc);
raw_spin_unlock(&desc->lock);
handle_irq_event(desc);
raw_spin_lock(&desc->lock);
if (!(desc->istate & IRQS_DISABLED) && desc->action) {
if (irqd_irq_masked(&desc->irq_data))
unmask_irq(desc);
}
out_unlock:
raw_spin_unlock(&desc->lock);
}
```
核心流程在第一行就执行mask_ack_irq。这是电平触发与边沿触发的关键区别:在运行任何处理函数之前,先屏蔽中断源并确认ACK。为什么必须mask?因为电平触发的信号线在设备服务完毕前持续有效,如果不mask,CPU在开中断后或handler返回瞬间再次被同一中断打断,形成无限递归的中断风暴。mask操作切断了信号线到CPU的路径。
mask_ack_irq函数的实现:
```c
void mask_ack_irq(struct irq_desc *desc)
{
struct irq_chip *chip = desc->irq_data.chip;
if (chip->irq_mask_ack) {
chip->irq_mask_ack(&desc->irq_data);
return;
}
if (chip->irq_mask)
chip->irq_mask(&desc->irq_data);
if (chip->irq_ack)
chip->irq_ack(&desc->irq_data);
}
```
优先使用芯片提供的复合操作chip->irq_mask_ack,因为部分硬件可以在一条寄存器指令中同时完成mask和ack,减少了寄存器读写次数。若芯片未提供复合操作,则分别调用irq_mask和irq_ack。
unmask操作的处理是电平触发的另一关键点。handle_irq_event返回后,desc->lock重新被获取,检查条件后调用unmask_irq:
```c
if (!(desc->istate & IRQS_DISABLED) && desc->action) {
if (irqd_irq_masked(&desc->irq_data))
unmask_irq(desc);
}
```
unmask必须在设备已将中断信号线恢复到非有效电平之后执行。如果设备驱动在handler内正确清除了中断源(写设备寄存器清除中断标志),信号线回到无效状态,此时unmask是安全的。如果设备未能清除中断源,unmask后中断线仍然有效,将立即重新触发mask_ack_irq序列,CPU陷入循环处理。这通常被视为设备驱动bug。
IRQS_PENDING标志在电平触发中的含义与边沿触发不同。当中断在disable状态下到达时,handle_level_irq设置IRQS_PENDING:
```c
if (unlikely(irqd_irq_disabled(&desc->irq_data))) {
desc->istate |= IRQS_PENDING;
goto out_unlock;
}
```
但这个pending在电平触发中不会被二次消费——它仅标记该中断在mask状态下曾经到达。当中断通过enable恢复后,__enable_irq检查IRQS_PENDING并调用desc->handle_irq重新处理。对于电平触发中断,重新处理意味着重新执行mask_ack_irq和handle_irq_event。
电平触发中断的IRQ_WAITING检测机制与边沿触发类似,在中断注册阶段设置IRQS_WAITING标志,handle_level_irq在顶部清除:
```c
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
```
区别在于电平触发的IRQ_WAITING不仅仅是首次中断确认。当电平触发中断的action链表为空时,中断处理会直接将pending中断丢弃但不屏蔽——这与边沿触发不同,边沿触发在action为空时会调用mask_irq永久屏蔽。电平触发之所以不永久屏蔽,是因为电平信号线可以被动监控,设备驱动注册后自然能收到下一次中断。
handle_level_irq与handle_fasteoi_irq的对比值得注意。handle_fasteoi_irq是为支持EOI(End of Interrupt)机制的中断控制器(如GIC)设计的,它不需要显式mask,而是通过EOI通知控制器中断处理结束。但在语义上,fasteoi等效于电平触发——控制器在EOI前不会再次提交相同中断。mask_ack_irq在fasteoi中退化为仅仅执行mask操作,ack由后续的EOI替代。
对于共享中断IRQF_SHARED,电平触发要求所有共享设备的中断触发类型一致。如果共享中断线上任一设备是边沿触发,则不能与电平触发设备共享。这是硬件电气特性决定的——电平信号线是"线与"逻辑,只要有一个设备保持有效电平,中断线持续有效,无法区分是哪个设备触发了中断,因此必须逐个调用action链表处理函数进行polling。
Linux handle_level_irq电平触发与mask_ack_irq序列
张小明
前端开发工程师
从零开始实战:用Python爬取京东图书“Python”关键词的价格、书名与评论数(附完整代码)
前言:为什么选择爬取京东图书? 在数据分析、市场调研或购书决策中,掌握图书的真实价格和用户反馈至关重要。京东图书作为国内领先的正版图书平台,拥有海量书籍和实时更新的价格。然而,手动复制粘贴几千条数据显然不现实。于是,编写一个自动化的网络爬虫就成了高效解决方…
避开UDS诊断的‘暗坑’:0x87链接控制服务常见NRC错误码分析与实战排错
避开UDS诊断的‘暗坑’:0x87链接控制服务常见NRC错误码分析与实战排错 在汽车电子诊断领域,0x87链接控制服务就像一位沉默的交通指挥员,它不直接参与数据传输,却决定着通信能否高效进行。许多工程师第一次遇到NRC 0x22或0x24时&am…
Mi-Create技术架构解析:构建小米穿戴设备表盘设计的完整工作流解决方案
Mi-Create技术架构解析:构建小米穿戴设备表盘设计的完整工作流解决方案 【免费下载链接】Mi-Create Unofficial watchface creator for Xiaomi wearables ~2021 and above 项目地址: https://gitcode.com/gh_mirrors/mi/Mi-Create 在智能穿戴设备生态快速发展…
拆解电力四遥:遥测、遥信、遥控、遥调基础知识
本来想写一篇IEC104的文章,仔细思考觉得应该先介绍四遥。四遥是电力调度自动化、SCADA、RTU的关键数据应用类型。本文用通俗语言,帮助大家理解四遥的基本知识。一、四遥是什么四遥,包括遥测、遥信、遥控、遥调四大功能,是通过技术…
Keras Callbacks实战指南:构建高效稳定的神经网络训练流程
1. 为什么你训练模型时总在“等”——Keras Callbacks 不是锦上添花,而是生产级训练的呼吸阀你有没有过这样的经历:凌晨两点,盯着 Jupyter Notebook 里model.fit()那行代码,光标在进度条末尾缓慢跳动,而你心里盘算着—…
map、filter、reduce:JavaScript数组处理的三大核心范式
1. 这三个函数不是语法糖,而是思维范式的分水岭 你刚学编程时,大概率是从 for 循环开始的:遍历数组、逐个处理、手动推结果。我带过不少转行学员,他们写一个“把所有用户名转大写再筛选出长度大于5的”需求,本能反应…