本文是《request_irq 成功却收不到中断》系列第 1 篇。一条 IRQ 线上挂着两个设备。其中一个的 handler 里状态位的掩码抄的是旧版手册irqreturn_tmy_handler(intirq,void*dev_id){u32 statusreadl(dev-regsSTATUS_REG);if(!(statusMY_INT_MASK))/* 掩码来自旧版手册 */returnIRQ_NONE;/* 每次都说不是我的 */...}这个设备从此再没处理过一次中断。奇怪的是 dmesg 干净得像没出过事没有nobody cared没有Disabling IRQ/proc/interrupts里那一行的计数还在稳定上涨。如果这条线上只有它一个设备同样的 bug 会在十万次中断之后被内核点名irq 42: nobody cared (try booting with the irqpoll option) Disabling IRQ #42同一份 handler同一个 bug换一条线结局两样——独占线上等得到内核开口共享线上永远不会被点名。场景由反复出现的典型故障模式归纳非具体设备记录下文机制均可核对。第一反应基本都是错的面对设备不工作但日志干净第一反应通常是内核总该发现没人处理吧。这个判断在独占线上成立在共享线上不成立——它不是一个报得慢的问题而是一条根本不存在的上报通道线型同类 bug 的暴露形式独占十万次后nobody caredDisabling IRQ #N共享同线有别的 handler 在认领 无日志、无计数、无状态位一句反证/proc/interrupts里那一行的计数是整条线的、按 CPU 分列行名只是 handler 的名字不是它的命中次数kernel/irq/proc.c:498-501。所以它一直在涨看起来很正常——那是邻居的贡献。真正的原因检测看的是聚合值不是你的返回值内核的虚警检测入口是note_interrupt()kernel/irq/spurious.c:272。它不在每个 handler 返回时被调用而是在整条线的所有 handler 都跑完之后才被调用一次拿到的是聚合结果kernel/irq/handle.c:200-201。聚合只有一行kernel/irq/handle.c:185retval|res;/* 把每个 handler 的返回值并进来 */三个返回值的位表示决定了这一行怎么工作include/linux/irqreturn.h:12-14IRQ_NONE 0IRQ_HANDLED 1IRQ_WAKE_THREAD 2。位或的后果是——只要有一位是 1整体就非 0。而拒认计数的门槛写在统计层kernel/irq/spurious.c:385只有聚合值恰好等于IRQ_NONE也就是全链都拒认时才累加。把两者拼起来看你的返回邻居的返回聚合值统计层看到什么IRQ_NONEIRQ_NONE0进入拒认计数十万次后禁用IRQ_NONEIRQ_HANDLED1认为有人管了你的拒认消失IRQ_NONEIRQ_WAKE_THREAD2同上判据是确定的不是有时候共享线上有 N 个 handler只要其中 N−1 个正常剩下那 1 个的持续拒认永远进不了统计判定。共享线在真实系统里并不罕见——PCI 传统 INTx 线、ISA 线以及大量内核自带驱动已经先占了这条线的场景都算。把这条判定链完整画出来——返回值走出 handler 之后会经过什么、被谁看见图里最容易被忽略的是右下那格它不是报得慢而是没有暴露通道——日志加不出来你不能给内核的统计层加计数判定只能前移到代码审查。底部那条出口与线型无关返回值落在三个枚举之外最常见的来源是忘了写return会被识别为bogus return value最多报 100 次后限流静默kernel/irq/spurious.c:227。把视角沉到源码层——上一张图里那句位或聚合在分发链上的位置是这样链上每一环都有行号可查。值得停一停的是倒数第二环handler 执行完后内核先做逐项判定越界、WAKE_THREAD 检查然后才是那一行位或——你在 handler 里返回的值只是聚合的一个输入。可操作今天就能查的四条先数线上有几个 handlergrep -E ^ *[0-9]: /proc/interrupts看目标 IRQ 那一行出现了几个名字。有邻居就不要再等日志。检查返回值有没有依据handler 里若没有任何寄存器读取、或无条件返回固定值直接标红——if (!(status MASK)) return IRQ_NONE;与if (status MASK) return IRQ_NONE;一字之差、语义相反编译器不会提醒你。给拒认分支自建计数内核不提供某个 handler 拒认了多少次这个计数器要让它在运行期可见就得在模块里自己建。做法是一个atomic_t配一个 debugfs 或/proc入口——不要写成每次拒认都pr_info高频线上会把 dmesg 刷爆。只用/proc/interrupts判断 handler 是否工作是错的它的计数归整条线所有。前三条查完你大概会想知道那套十万次才报的统计到底怎么算的为什么是十万次没展开的部分虚警统计本身还有一整套阈值与两个计数为什么是十万次、99.9%占比、100ms窗口/proc/irq/N/spurious里irq_count与irqs_unhandled两条计数的读法正好相反混用会算出完全错误的结论。这些本文没有展开。下一篇下一篇就聊这三个数怎么来的nobody cared为什么非等十万次以及那条容易读反的计数——很多人以为早就该禁用了其实那个计数一直在被悄悄重置。回复「交流」我很乐意分享更精彩的内容。感谢阅读。转载请注明出处与作者。