中断上半部(硬中断)本身必须极短、无阻塞、无复杂逻辑,提升响应时间的本质是:把能移到下半部 / 线程的全移走,只留最必要的硬件清中断 + 标记事件,再配合硬件、驱动、内核配置做极致裁剪。
先明确:上半部能做什么、绝对不能做什么
上半部(irq handler)原则:
只做 3 件事
- 读取中断状态(确认是本设备中断)
- 清中断标志(硬件级 ack,避免死循环)
- 唤醒下半部:
tasklet/workqueue/threaded irq
上半部严禁
- 任何阻塞操作:
msleep、mutex、copy_from/to_user、kmalloc(GFP_KERNEL) - 复杂循环、大量计算、日志打印(
printk极慢) - 关中断时间过长(
local_irq_save尽量少用、极短) - 同一中断线共享逻辑臃肿
- 任何阻塞操作:
只要上半部超过几微秒,就是设计问题,优先重构代码。
驱动层:最直接、收益最大的优化
1. 强制使用线程化中断(threaded irq)
把绝大部分逻辑放到线程上下文,上半部只做判断和唤醒:
// 上半部:极简 static irqreturn_t demo_irq_handler(int irq, void *dev_id) { if (!intr_pending(dev)) return IRQ_NONE; /* 仅清中断 */ clear_intr(dev); return IRQ_WAKE_THREAD; // 直接唤醒线程 } // 下半部:真正的处理逻辑 static irqreturn_t demo_irq_thread(int irq, void *dev_id) { /* 读数据、处理、协议、提交、调度... 所有逻辑放这 */ return IRQ_HANDLED; } // 注册时: ret = devm_request_threaded_irq(dev, irq, demo_irq_handler, // 上半部:极短 demo_irq_thread, // 线程:可调度、可阻塞 IRQF_ONESHOT, "demo_irq", dev);效果:上半部响应时间直接压到<1µs 级别。
2. 禁用 / 精简共享中断逻辑
- 尽量使用独占有中断线(非共享),避免遍历所有共享 handler
- 若必须共享:尽早判断,不是自己中断立刻返回
IRQ_NONE
3. 去掉上半部所有 printk
pr_info/pr_err在中断上下文会同步写串口 /console,毫秒级延迟- 调试用:改用
trace_printk或 eBPF 打点,生产环境全删
4. 最小化寄存器读写 & 循环
- 合并连续 MMIO 写(必要时加屏障)
- 上半部不要循环轮询状态,轮询一律丢到下半部或定时器
内核配置与调度:降低系统干扰
1. 打开PREEMPT_RT(实时补丁)最有效
如果你追求硬实时、稳定低延迟:
- 启用
CONFIG_PREEMPT_RT - 中断线程化为普通优先级线程,可以绑定 CPU、调高优先级
无 RT 补丁时至少选:
CONFIG_PREEMPT(完全可抢占内核)- 关闭
CONFIG_PREEMPT_VOLUNTARY、CONFIG_PREEMPT_NONE
2. 中断绑定(SMP 亲和性)
- 把高速 / 关键中断绑到独立核心,避免与用户态、其他中断争抢
echo 2 > /proc/irq/<irq_num>/smp_affinity_list- 关键中断独占一个 CPU core,并隔离该核:
isolcpus=2等
3. 关闭不必要的内核功能
这些都会增加中断路径开销:
- 关闭 debug:
CONFIG_DEBUG_INFO、CONFIG_LOCKDEP、CONFIG_PROVE_LOCKING - 关闭 tracing、perf 采样、ftrace(生产环境)
- 关闭 KASLR、KPTI(如安全允许,可略微降低开销)
CPU 与硬件层面优化
1. 禁用 CPU 深度休眠(C-state)
C-state 切换会导致中断响应毛刺、几十微秒抖动:
- 内核参数:
idle=poll或intel_idle.max_cstate=1 - 或在 BIOS 关闭 C3、C6、C7 等深度休眠
2. 固定 CPU 频率,禁用睿频 / 节能
performance调频策略:
cpupower frequency-set -g performance- 禁用睿频、turbo boost,减少频率波动带来的延迟抖动
3. 关闭不必要的外设与共享中断
- 多余串口、并口、ACPI 不必要事件、键盘鼠标中断
- 网卡 / 存储设备开启MSI/MSI-X(比传统中断快很多,天然不共享)
极简优化 Checklist
- 上半部代码控制在10 行以内,只清中断 + 唤醒线程
- 全线使用
request_threaded_irq - 上半部无 printk、无循环、无等待、无分配
- 关键中断绑独立 CPU,内核
isolcpus隔离 - 开启
CONFIG_PREEMPT或直接上PREEMPT_RT - 关闭 CPU 深度 C-state、频率固定为 performance
- 设备尽量用MSI/MSI-X,避免共享中断
- 用 ftrace 量化耗时,确保上半部 < 1~2µs