1. Hung Task 概述

Hung Task(挂起任务)是 Linux 内核调度器与进程管理子系统中的一种关键异常状态。当内核线程或用户态进程在不可中断的睡眠状态(D 状态)下持续超过 120 秒(默认阈值)且未被调度器唤醒时,内核会判定该任务为 Hung Task,并触发相应的检测与告警机制。理解 Hung Task 的本质、影响及常见场景,是掌握调度器故障定位的第一步。

核心定义: Hung Task 并非任务“死机”或“崩溃”,而是任务陷入 D 状态(TASK_UNINTERRUPTIBLE) 且长时间无法被调度器切换出去或唤醒,导致该任务“挂死”在等待队列中。

1.1 什么是 Hung Task

在 Linux 内核中,进程状态分为多种,其中与 Hung Task 直接相关的是 TASK_UNINTERRUPTIBLE(D 状态)。当任务处于 D 状态时,它正在等待某种内核资源(如磁盘 I/O、锁、内存页)的完成,且在此期间不响应任何信号。正常情况下,这种等待是短暂的。但当底层资源出现故障或死锁时,任务可能永久停留在 D 状态,超过内核设定的超时阈值(默认 120 秒)后,即被判定为 Hung Task。

内核通过 hung_task 内核线程定期扫描所有任务,检查处于 D 状态超过阈值的任务,并输出告警日志。该机制由内核配置参数控制:

内核参数 默认值 说明
kernel.hung_task_timeout_secs 120 Hung Task 检测超时时间(秒),设为 0 可关闭检测
kernel.hung_task_warning 1 是否输出 Hung Task 告警日志(0=关闭,1=开启)
kernel.hung_task_panic 0 检测到 Hung Task 时是否触发内核 panic(0=仅告警,1=panic)
kernel.hung_task_check_count 4194304 每次扫描检查的最大任务数
排查提示: 生产环境中建议将 kernel.hung_task_panic 保持为 0,避免因 I/O 抖动导致整机 panic。但若系统对可用性要求极高且需要快速故障转移,可考虑开启 panic 配合 kdump 捕获内存转储。

以下 SVG 展示了 Hung Task 从任务挂起到内核检测再到告警输出的完整生命周期:

Hung Task 检测生命周期 任务正常运行 等待内核资源 进入 D 状态 超时检测(120s) 输出告警日志 触发 kernel panic 关键说明: • D 状态任务不响应任何信号,无法通过 kill -9 强制终止 • 检测线程由 hung_task 内核线程执行,默认每 120 秒扫描一次 • 若配置 panic,系统将立即崩溃并触发 kdump 转储

1.2 Hung Task 对系统的影响

Hung Task 对系统的影响是多维度的,从单个任务的挂死到整个系统的不可用,其危害程度取决于挂死任务的角色和数量。以下是主要影响维度:

影响维度 具体表现 严重程度
任务级 单个进程/线程永久挂死,无法响应信号,无法被 kill 低
资源级 挂死任务持有的锁、内存、文件描述符无法释放,导致其他任务阻塞 中
服务级 关键服务进程(如数据库、Web 服务器)挂死,导致服务中断 高
系统级 大量 D 状态任务堆积,系统负载飙升,新进程无法创建,最终系统不可用 极高
内核级 若配置 panic,检测到 Hung Task 后系统直接崩溃重启 极高
注意: 在云原生环境中,一个 Hung Task 可能导致整个 Pod 无法正常终止(因为 D 状态任务不响应 SIGTERM/SIGKILL),进而影响 Kubernetes 的 Pod 生命周期管理和滚动更新流程。

以下 SVG 展示了 Hung Task 在系统中的连锁影响效应:

Hung Task 连锁影响效应 Hung Task 挂死 资源无法释放 其他任务阻塞 服务中断 系统负载飙升 新进程创建失败 系统不可用

1.3 常见的 Hung Task 场景

在实际生产环境中,Hung Task 的出现往往与底层存储、文件系统或内核锁的异常有关。以下是经过大量故障案例总结的常见场景:

  • 存储 I/O 挂起: 网络存储(NFS、iSCSI、FC-SAN)链路中断或存储设备无响应,导致 D 状态任务在等待 I/O 完成时永久阻塞。这是最常见的 Hung Task 场景。
  • 文件系统元数据锁冲突: 在 XFS、ext4 等文件系统中,当多个进程并发操作同一文件或目录时,可能因元数据锁(如 inode 锁、日志锁)死锁导致任务挂死。
  • 内存回收死锁: 当系统内存严重不足时,内核内存回收机制(kswapd)可能因等待某个进程释放内存页而陷入死锁,导致大量 D 状态任务堆积。
  • 设备驱动 Bug: 某些块设备驱动或网络驱动在特定条件下无法完成 I/O 请求,导致等待该驱动的任务永久挂起。
  • 内核锁竞争: 内核中的自旋锁、互斥锁或读写锁在极端并发场景下可能产生死锁,导致持有锁的任务和等待锁的任务同时挂死。
  • Cgroup 冻结操作: 在容器环境中,对 cgroup 进行冻结(freeze)操作时,如果 cgroup 内的进程处于 D 状态,冻结操作可能无法完成,导致整个 cgroup 挂起。
实战统计: 根据某大型互联网公司的故障数据,约 70% 的 Hung Task 由存储 I/O 问题引起,15% 由文件系统锁问题引起,10% 由内存回收问题引起,其余 5% 由驱动 Bug 和其他原因引起。

以下 SVG 展示了常见 Hung Task 场景的分布与关联关系:

常见 Hung Task 场景分布 存储 I/O 挂起 占比 ~70% 文件系统锁冲突 占比 ~15% 内存回收死锁 占比 ~10% 设备驱动 Bug 占比 ~3% 内核锁竞争 占比 ~2% 存储 I/O 挂起是最常见的 Hung Task 场景,排查时应优先检查存储链路状态 和文件系统 I/O 等待时间(/proc/stat 中的 iowait 指标)。
排查切入点: 当系统出现 Hung Task 告警时,首先通过 dmesg 查看内核日志中的 hung_task 告警信息,定位挂死任务的 PID 和函数调用栈。然后根据调用栈中的函数名判断是等待 I/O、等待锁还是等待内存,从而确定故障根因。