Linux 服务器 D 状态进程排查与处理

Linux 服务器 D 状态进程排查与处理

D 状态(也叫 uninterruptible sleep、Disk sleep)是 Linux 进程状态中最让人头疼的一种。一旦出现大量或持续的 D 状态进程,通常意味着系统正经历严重的阻塞,最常见的表现就是:

  • load average 异常高
  • iowait / wa 显著升高
  • 响应变慢甚至假死(ssh 卡顿、命令迟迟无返回)
  • top/htop 里看到大量进程状态为 D

一、D 状态到底是什么?(内核视角)

在 Linux 任务(task_struct)中,进程状态(task->state)有几种主要取值:

  • R → Running / Runnable(可运行或正在运行)
  • S → Interruptible sleep(可被信号中断的睡眠,例如等待网络、锁、sleep)
  • D → Uninterruptible sleep(不可被信号中断的睡眠)
  • T → Stopped(被 SIGSTOP 暂停)
  • Z → Zombie(僵尸进程)

D 状态的核心定义

进程正在等待内核中某个 不可中断 的 I/O 操作完成,且故意不响应任何信号(包括 SIGKILL),直到该 I/O 操作结束或出错。

换句话说: 只要进程进入了 D 状态,kill -9 也杀不掉它,只能等内核把这次 I/O 做完(或超时/出错)。

二、哪些情况会导致进程进入 D 状态?(最常见 Top 10)

排名场景类型典型表现(iostat / 其他指标)占比(经验)是否可被 kill -9 杀死是否会引起系统假死 / 高 load
1磁盘 / 文件系统 IO 阻塞iostat %util 高、await 极高★★★★★是(最常见)
2NFS / 网络文件系统挂起NFS 服务器宕机 / 网络断开★★★★☆是(经典“卡死”场景)
3脏页回写压力过大(kswapd / pdflush)Dirty + Writeback 几 GB 且持续上升★★★★
4块设备驱动卡住(坏块、超时)dmesg 出现 I/O error / timeout★★★
5某些文件系统元数据操作卡住高并发小文件场景,dentry/inode 操作阻塞★★★部分场景
6swap 设备(机械盘 / 慢盘)读写慢si/so 非零且持续,swap 设备 %util 高★★☆
7某些虚拟化 / 容器存储驱动问题virtio_blk / nvme 队列满 / 宿主机存储慢云主机常见
8dm-crypt / LUKS 加密设备卡住加密盘性能瓶颈或密码输入超时少见
9FUSE 文件系统(如 s3fs、gluster)挂起FUSE 进程本身卡住或网络问题少见
10某些第三方内核模块 bugdmesg 出现模块相关错误极少

三、D 状态进程排查标准流程(推荐顺序)

  1. 先确认 D 状态进程数量与持续时间
    Bash
    # 当前有多少个 D 状态进程
    ps -eo pid,ppid,state,cmd | grep ' D' | wc -l
    
    # 列出所有 D 进程详细信息(最常用)
    ps -eo pid,ppid,state,lstart,time,cmd | grep ' D'
    
    # 看哪些进程的父进程是谁(常用于找罪魁祸首)
    ps -eo pid,ppid,state,cmd | awk '$3=="D" {print $2 " " $1 " " $4}' | sort | uniq -c | sort -nr
  2. 看 iowait 与磁盘饱和度(90% 以上 D 状态都跟这个有关)
    Bash
    iostat -xmdz 1 5          # %util、await、avgqu-sz、r/s、w/s
    vmstat 1 5                # wa 列、b 列(b 就是 D 状态进程数)
  3. 定位具体是哪个磁盘、哪个进程在制造大量 IO
    Bash
    iotop -o -P               # 实时显示有 IO 的进程(最直观)
    pidstat -d 1 5            # 进程级读写统计
  4. 检查是否有被删除但仍在写的文件(deleted but open)
    Bash
    lsof +L1 | grep deleted
    # 或
    lsof -p PID | grep '(deleted)'
  5. 查看内核日志,看是否有 I/O 错误或超时
    Bash
    dmesg -T | grep -i -E 'error|timeout|I/O|failed|hung|hung_task'
    journalctl -k -p warning..crit --since "1 hour ago"
  6. 如果是 NFS / 网络存储
    Bash
    nfsstat -m                # 查看 NFS 挂载点状态
    mount | grep nfs
    showmount -e nfs_server_ip

四、处理 D 状态进程的正确优先级(从安全到激进)

原则D 状态进程 kill -9 是无效的,只能等内核完成或放弃这次 I/O。

优先级处理方式适用场景风险级别备注
1等待(最安全)D 状态进程数量少、业务可忍受★☆☆☆☆通常 几秒~几分钟会自行恢复
2优化业务写模式日志级别太高、sync_binlog=1、appendfsync always★★☆☆☆异步日志、调 innodb_flush_log_at_trx_commit=2
3杀掉产生大量 IO 的上游进程找到罪魁祸首(如某个疯狂写日志的 worker)★★★☆☆kill -9 上游进程,D 状态下游进程会自然退出
4重启相关服务(优雅)服务支持平滑重启(nginx reload、systemctl restart)★★★★☆D 状态的 worker 进程会随服务退出而消失
5echo b > /proc/sysrq-trigger(魔法键)整个系统卡死,无法 ssh 或执行命令★★★★★强制同步 + 重启,数据可能丢失,慎用
6硬重启机器所有办法无效,业务可接受宕机★★★★★最后手段,通常伴随数据损坏风险

五、预防 D 状态大量出现的常用措施

  1. 日志类
    • 降日志级别(info → warn)
    • 使用异步日志库(log4j2 AsyncAppender、logback AsyncAppender)
    • logrotate + copytruncate 而不是 mv + create
  2. 数据库
    • innodb_flush_log_at_trx_commit = 2(非严格一致性可接受)
    • sync_binlog = 1000 或 0
    • 开启 innodb_doublewrite = 1(但权衡性能)
  3. 系统参数
    • vm.dirty_ratio / vm.dirty_background_ratio 调小,避免脏页堆积
    • vm.dirty_expire_centisecs 调小,加快过期脏页回写
  4. 监控告警
    • D 状态进程数 > 5 持续 1min 告警
    • iowait > 30% 持续 3min 告警
    • 单个盘 %util > 85% 持续 5min 告警

一句话总结

D 状态 = 进程在等待不可中断的块设备 I/O,且故意不响应任何信号。 iowait 高 + 大量 D 状态 → 几乎一定是磁盘或文件系统层面的阻塞。 先找哪个进程在写/读 → 再看是日志、数据库、还是网络存储 → 最后优化写策略或隔离 IO

Telegram
Telegram@IDCSELL