3、Hung Task检测机制:内核hung_task检测模块、NMI Watchdog与Soft Lockup、Hard Lockup的区别

在Linux内核中,当调度器或某个执行路径长时间无法响应调度事件时,系统可能陷入“假死”状态。为了及时发现并定位这类故障,内核提供了三种不同粒度的检测机制:Hung Task、Soft Lockup 和 Hard Lockup。它们分别从进程级、内核抢占级和CPU中断级进行监控,构成了调度器故障定位的“三道防线”。

3.1 Hung Task检测模块

Hung Task机制是内核中专门用于检测处于不可中断睡眠状态(D状态)且长时间未调度的进程。其核心原理是内核线程 khungtaskd 周期性扫描所有进程,检查是否有进程在D状态下停留超过预设阈值。

关键参数:
  • kernel.hung_task_timeout_secs:默认120秒,超过此时间未调度的D状态进程将被判定为Hung Task。
  • kernel.hung_task_warnings:默认10次,控制日志输出频率,防止日志风暴。
  • kernel.hung_task_panic:默认0,若设置为1,检测到Hung Task时直接触发panic。

当检测到Hung Task时,内核会在 /var/log/messages 或通过 dmesg 输出类似以下日志:

[ 1234.567890] INFO: task kworker/u:2:456 blocked for more than 120 seconds.
[ 1234.567891]       Tainted: G           O
[ 1234.567892] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 1234.567893] kworker/u:2     D    0   456      2 0x00000000
[ 1234.567894] Call Trace:
[ 1234.567895]  __schedule+0x2d0/0x8a0
[ 1234.567896]  schedule+0x35/0x90
[ 1234.567897]  io_schedule+0x15/0x40
[ 1234.567898]  wait_on_page_bit_common+0x1a0/0x3e0
[ 1234.567899]  __lock_page+0x80/0xb0
[ 1234.567900]  filemap_fault+0x3a0/0x7a0
[ 1234.567901]  __do_fault+0x38/0x120
[ 1234.567902]  handle_mm_fault+0x1b0/0x300
[ 1234.567903]  do_page_fault+0x2e0/0x4f0
[ 1234.567904]  page_fault+0x1e/0x30
排查要点:日志中的 Call Trace 是定位阻塞点的关键。常见的阻塞点包括 wait_on_page_bit(页缓存等待)、inode_sleep(inode锁等待)、rpc_wait(NFS RPC等待)等。结合 /proc/<pid>/stack 可以实时查看进程内核栈。

3.2 Soft Lockup与Hard Lockup

Soft Lockup和Hard Lockup是内核用于检测CPU是否“卡死”的机制,它们依赖于NMI(Non-Maskable Interrupt,不可屏蔽中断)和周期性时钟中断。

特性 Soft Lockup Hard Lockup
检测对象 内核态是否长时间禁止抢占(关闭内核抢占) CPU是否响应中断(NMI)
触发条件 单个CPU上超过20秒未发生调度切换 单个CPU上超过20秒未响应NMI中断
检测手段 高精度定时器(hrtimer) + 内核线程 watchdog NMI中断 + 硬件性能计数器(PMU)
典型原因 自旋锁死循环、关抢占后执行耗时操作 中断被长时间屏蔽、硬件故障、CPU挂起
内核参数 kernel.softlockup_panic kernel.hardlockup_panic
日志特征 watchdog: BUG: soft lockup - CPU#0 stuck for 22s! watchdog: BUG: hard lockup - CPU#0 stuck for 22s!
重要区别:Soft Lockup发生时,系统可能还能响应部分中断(如NMI),因此可以通过NMI触发panic或输出调试信息。而Hard Lockup发生时,CPU完全无法响应任何中断,包括NMI,此时只能依赖硬件看门狗(如HPET或外部WDT)来复位系统。

3.3 三种检测机制的关系与对比

从检测层次上看,Hung Task、Soft Lockup和Hard Lockup构成了从进程到CPU的完整监控链:

内核调度器故障检测机制层次图 Hung Task 检测 进程级:D状态进程超时未调度 Soft Lockup 检测 内核抢占级:CPU未发生调度切换 Hard Lockup 检测 中断级:CPU未响应NMI中断 检测粒度逐渐降低 严重程度逐渐升高 响应方式 日志 → panic → 硬件复位 故障定位建议 先检查Hung Task日志定位阻塞进程 再结合Soft/Hard Lockup判断是否涉及CPU死锁

3.4 检测机制的配置与调优

在实际生产环境中,需要根据业务场景合理配置这三种检测机制的参数,避免误报或漏报:

参数 默认值 推荐生产值 说明
kernel.hung_task_timeout_secs 120 300 对于IO密集型业务(如数据库),适当增大避免误报
kernel.hung_task_panic 0 0 生产环境建议关闭,避免因IO抖动导致系统panic
kernel.softlockup_panic 0 1 建议开启,Soft Lockup通常意味着内核bug
kernel.hardlockup_panic 0 1 建议开启,Hard Lockup意味着CPU完全失控
kernel.watchdog_thresh 10 10 Soft/Hard Lockup的超时阈值(秒),实际检测为2倍值
调优实践:对于运行在虚拟化环境或云主机上的系统,由于存在CPU抢占(Steal Time),建议将 kernel.watchdog_thresh 适当增大至20-30秒,避免因宿主机调度延迟导致误报Soft Lockup。

3.5 日志分析实战:从dmesg中识别故障类型

当系统出现异常时,dmesg 日志是定位问题的第一手资料。以下通过三个典型日志片段,展示如何区分三种检测机制的触发场景:

  1. Hung Task日志特征:
    [ 5000.123456] INFO: task mysqld:12345 blocked for more than 120 seconds.
    [ 5000.123457] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
    [ 5000.123458] mysqld         D    0 12345      1 0x00000000
    [ 5000.123459] Call Trace:
    [ 5000.123460]  __schedule+0x2d0/0x8a0
    [ 5000.123461]  schedule+0x35/0x90
    [ 5000.123462]  io_schedule+0x15/0x40
    [ 5000.123463]  wait_on_page_bit_common+0x1a0/0x3e0

    → 进程处于D状态,等待页缓存IO完成,属于IO阻塞。

  2. Soft Lockup日志特征:
    [ 6000.654321] watchdog: BUG: soft lockup - CPU#2 stuck for 22s! [kworker/2:1:456]
    [ 6000.654322] Modules linked in: ...
    [ 6000.654323] CPU: 2 PID: 456 Comm: kworker/2:1 Tainted: G           O
    [ 6000.654324] RIP: 0010:_raw_spin_lock+0x1c/0x30
    [ 6000.654325] Call Trace:
    [ 6000.654326]  spin_lock+0x2a/0x30
    [ 6000.654327]  ext4_do_update_inode+0x1e0/0x2a0

    → CPU#2在自旋锁上卡住超过22秒,内核抢占被关闭,属于内核态死锁。

  3. Hard Lockup日志特征:
    [ 7000.111111] watchdog: BUG: hard lockup - CPU#3 stuck for 22s! [swapper/3:0]
    [ 7000.111112] Modules linked in: ...
    [ 7000.111113] CPU: 3 PID: 0 Comm: swapper/3 Tainted: G           O
    [ 7000.111114] RIP: 0010:native_safe_halt+0x6/0x10

    → CPU#3在空闲循环中无法响应NMI,可能是硬件故障或中断控制器异常。

故障定位三步法:
  1. 看类型:日志开头是 INFO: task(Hung Task)、soft lockup 还是 hard lockup。
  2. 看CPU和进程:锁定出问题的CPU编号和进程名/进程ID。
  3. 看调用栈:分析 Call Trace 中的最后一个内核函数,定位阻塞点。

3.6 三种机制的协同工作流程

当系统出现故障时,三种检测机制可能同时触发,但它们的触发顺序和严重程度不同。以下流程图展示了典型的协同工作场景:

Hung Task / Soft Lockup / Hard Lockup 协同检测流程 系统运行中 khungtaskd 扫描进程 D状态进程超时? 输出Hung Task日志 记录进程调用栈 继续监控 等待下一周期 watchdog线程检查 CPU是否发生调度切换? 输出Soft Lockup日志 检查自旋锁/关抢占 NMI中断检查 CPU是否响应NMI? 输出Hard Lockup日志 硬件或中断故障 是 否 否 是 注意:Hard Lockup发生时,CPU可能无法输出任何日志, 此时需要依赖硬件看门狗(如IPMI)或串口控制台获取最后信息。
实战提醒:在排查系统“假死”问题时,如果同时看到Hung Task和Soft Lockup日志,优先分析Soft Lockup的调用栈,因为它通常揭示了导致所有进程阻塞的根本原因(如全局自旋锁死锁)。Hung Task往往是Soft Lockup的“症状”而非“病因”。