NMI watchdog是Linux内核中用于检测CPU锁死(lockup)的一种监控机制。它利用硬件提供的不可屏蔽中断(NMI)周期性地检查每个CPU是否还在正常调度。当一个CPU核心长时间不响应中断、陷入死循环,或者关中断时间过长时,NMI watchdog会及时发现问题并输出诊断信息,甚至主动触发panic让系统留下崩溃现场。对于排查驱动缺陷、固件问题或者极端负载下的内核卡死,这个工具往往能提供最直接的线索。

NMI watchdog的工作原理
要理解NMI watchdog的配置,首先需要弄清楚它背后的运作方式。内核中存在两种锁死检测:软锁检测(soft lockup)和硬锁检测(hard lockup)。软锁检测依赖时钟中断,每个CPU上的时钟中断会更新一个时间戳,同时watchdog内核线程会定期检查这个时间戳。如果发现时间戳超过一定阈值没有更新,说明该CPU长时间没有响应时钟中断,判定为软锁。
硬锁检测则更进一步,它针对的是CPU连时钟中断都被关闭的场景,此时软锁检测本身也无法运行。硬锁检测依赖PMU(性能监控单元)产生的NMI,因为NMI不可被普通中断屏蔽,即使CPU处于关中断状态也能触发。watchdog通过perf事件在固定周期产生NMI,检查时钟中断的计数是否前进,如果没有前进就判定为硬锁并输出告警。
这两种机制是互补关系。软锁检测在几乎所有架构上都可用,而硬锁检测需要平台支持NMI或类似的不可屏蔽机制,x86平台通过perfctr实现,部分ARM平台则借助HPET、GIC等定时器模拟。理解这一点对后续配置参数很重要,因为不同平台能开启的能力是有差异的。
内核配置与启动参数
使用NMI watchdog前,需要确认内核编译选项已经打开。在内核配置中,相关的选项包括LOCKUP_DETECTOR、SOFTLOCKUP_DETECTOR和HARDLOCKUP_DETECTOR。大多数发行版的官方内核默认开启了这些选项,如果是自己编译内核,需要检查.config文件中这几项是否为y:
# 查看当前内核配置 grep -E "LOCKUP|WATCHDOG" /boot/config-$(uname -r) # 常见的相关选项 CONFIG_LOCKUP_DETECTOR=y CONFIG_SOFTLOCKUP_DETECTOR=y CONFIG_HARDLOCKUP_DETECTOR=y CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y
运行时控制主要靠内核启动参数和sysctl接口。常用的启动参数有这几个:nmi_watchdog=1表示使用基于perf的硬锁检测(默认值),nmi_watchdog=0表示完全关闭,nmi_watchdog=2表示使用I/O APIC模式,主要用于某些老平台。将这些参数追加到GRUB配置中即可生效:
# 编辑GRUB配置 vim /etc/default/grub # 在GRUB_CMDLINE_LINUX中加入参数 GRUB_CMDLINE_LINUX="... nmi_watchdog=1" # 重新生成GRUB配置 grub2-mkconfig -o /boot/grub2/grub.cfg # Debian/Ubuntu系统则执行 update-grub
除了启动参数,还可以在系统运行中通过sysctl动态调整。softlockup的阈值由kernel.watchdog_thresh控制,默认值为10秒,取值范围是0到60。设为0会同时禁用软锁和硬锁检测。需要注意的是,修改这个值会影响检测的灵敏度,设置得太小可能在高负载时产生误报,因为正常的负载高峰也可能让某个CPU的调度短暂延迟。
# 查看当前阈值 sysctl kernel.watchdog_thresh # 临时修改阈值为5秒 sysctl -w kernel.watchdog_thresh=5 # 永久生效,写入配置文件 echo "kernel.watchdog_thresh=5" >> /etc/sysctl.conf sysctl -p
sys接口与panic触发配置
内核还暴露了一套sys接口,位于/proc/sys/kernel/目录下,可以查看和调整watchdog的各项状态。其中watchdog文件控制整个机制的开关,watchdog_cpumask可以指定哪些CPU参与监控,这在NUMA机器或者CPU数量很多的服务器上很有用,可以避免watchdog线程占用过多资源。
# 查看watchdog整体开关 cat /proc/sys/kernel/watchdog # 只让前4个CPU参与监控 echo 0f > /proc/sys/kernel/watchdog_cpumask # 查看软锁panic开关 cat /proc/sys/kernel/softlockup_panic
单纯检测到锁死只会输出一条告警日志,如果想要求系统在发生锁死时立即panic以便用kdump抓取vmcore,需要打开panic开关。对应的两个文件是kernel.softlockup_panic和kernel.hardlockup_panic。将它们设为1后,一旦检测到锁死,内核会主动崩溃并按照kdump的配置生成转储文件,这样就能拿到锁死现场的完整调用栈和寄存器信息。
# 开启软锁与硬锁触发panic sysctl -w kernel.softlockup_panic=1 sysctl -w kernel.hardlockup_panic=1 # panic后10秒自动重启(按需设置) sysctl -w kernel.panic=10
拿到vmcore之后,用crash工具配合内核调试符号进行分析,重点看发生panic的那个CPU的backtrace。如果是硬锁,通常能看到CPU停在中断关闭的代码路径中,比如某个驱动长时间持有自旋锁;如果是软锁,多半是某个内核路径死循环或者调度被长时间禁止。这种分析思路比盲猜高效得多。
常见问题与排查技巧
实际配置中有几个高频问题值得注意。第一个是NMI watchdog与perf的冲突。硬锁检测占用了PMU的硬件资源,当用户程序尝试使用perf采集性能数据时,可能遇到资源被占用导致perf事件创建失败的报错。反过来,如果系统中有其他程序独占了PMU(比如某些监控agent),NMI watchdog的硬锁检测也会静默失效,此时dmesg中会出现相应提示。解决办法是权衡取舍,要么停用perf相关任务,要么用nmi_watchdog=0关闭硬锁检测。
# 检查watchdog状态,关注dmesg中的提示 cat /proc/sys/kernel/watchdog dmesg | grep -i -E "nmi|watchdog|perf" # 关闭硬锁检测但保留软锁检测 echo 0 > /proc/sys/kernel/nmi_watchdog
第二个常见问题是虚拟机环境。在许多云主机或KVM虚拟机中,PMU默认不可用或者被虚拟化层屏蔽,硬锁检测根本无法启用,dmesg里会提示硬件不支持。这种情况下软锁检测仍然有效,可以依赖kernel.softlockup_panic来兜底。第三个问题是误报,在极端负载或者CPU微码异常的机器上,可能出现周期性的watchdog告警但系统实际并未锁死,此时可以适当调大watchdog_thresh,或者先通过告警日志中的调用栈判断是否为同一处代码反复触发,再决定是否深入排查。
最后建议在排查内核卡死类问题时,把NMI watchdog、kdump、以及串口控制台日志三者配合使用。串口能捕捉到panic之前的最后输出,kdump能保留完整现场,watchdog则负责及时发现问题并触发前两者。这套组合拳在处理偶发性的线上挂死问题时,往往是最可靠的手段。配置完成后可以用模块化的方式先在测试环境验证一遍panic路径是否通畅,确认kdump能正常生成vmcore,再部署到生产环境,避免真正出事时才发现转储不可用。
NMI watchdog内核锁检测Linux内核调试修改时间:2026-09-05 01:54:36