load 飙到 50 但 CPU 几乎空闲:一次 D 状态进程与 load average 的复盘

监控报警一台 8 核服务器 load average 飙到 50 多还在涨,我以为 CPU 要烧了 top 一看 CPU 却 95 空闲根本没进程烧 CPU,负载极高加 CPU 极闲自相矛盾。排查梳理:uptime 看 load 1 分钟 51,top 看 %Cpu id 95 空闲 us sy 加起来不到 5,ps -eo state 统计发现 48 个 D 状态进程,列出来全在访问一个 NFS 网络盘,cat /proc/PID/wchan 看到卡在 nfs_wait_on_request,df 那个挂载点命令本身也卡住 NFS 挂死了;核心认知 load average 不是 CPU 使用率它统计的是系统里处于 R 正在跑或排队等 CPU 和 D 不可中断睡眠通常在等 IO 两种状态进程数量的时间平均,所以 load 高有两种病因 R 进程多是 CPU 不够用 D 进程多是 IO 阻塞,CPU 空闲加 load 高一点不矛盾那是 load 里被 IO 堵住的 D 进程在撑着,看 top 的 us sy id wa 区分;D 状态是不可中断睡眠进程发起底层 IO 后内核让它睡下等 IO 完成且这等待不可被任何信号打断因为 IO 在硬件层进行中中途打断会损坏数据,推论 D 状态进程连 kill -9 都杀不掉信号只能排队等它从 D 出来 IO 永不返回就永久卡死,短暂 D 是读写磁盘的正常必经态大量进程长期卡 D 才是病;定位 D 进程看 wchan 和 stack,wchan 是 nfs_ 查网络存储 io_schedule 查本地磁盘 ext4 xfs 查文件系统,iostat -x 看 %util 和 await dmesg 看 IO error 和 NFS 超时;根因常见来源 NFS 网络存储挂了该用 soft 挂载别 hard 死等、本地磁盘坏或慢 smartctl 查健康、磁盘 inode 满、IO 被某进程打满 iotop 抓大户;处理不是杀进程是修好 IO 源源一恢复 D 进程自动解开排队的 SIGKILL 才生效 load 回落,IO 源彻底回不来 umount -f -l 强卸实在解不开常只能重启整机。正确做法是 load 高 CPU 闲先查 D 状态再看 wchan 一路推到 IO 源,以及一套 load 与 D 状态排查纪律。

2022 年,一次"系统 load average 飙到了 50,我以为 CPU 要烧了,top 一看 CPU 却【95% 空闲】"的事故,把我对"负载"这件事的理解,从头到尾翻新了一遍。那天监控疯狂报警:某台 8 核的服务器,load average 一路冲到 50 多,还在涨。50 的负载在一台 8 核机器上,意味着"严重过载"——我脑子里立刻浮现出 CPU 被打满、风扇狂转的画面。我火急火燎登上去 top,准备抓那个吃满 CPU 的进程。结果 top 第一屏就把我看懵了:%Cpu(s) 那一行,id(空闲)是 95.3——CPU,九成五是【闲着】的。us(用户态)、sy(内核态)加起来不到 5%。我反复刷新 top,反复确认:这台机器的 CPU,确确实实【几乎没在干活】。可它的 load average,白纸黑字写着 50。我彻底糊涂了:负载,不就是"CPU 有多忙"吗?CPU 都闲成这样了,那个 50 的负载,到底是【谁】贡献的?系统里明明没有进程在烧 CPU,这"50 个单位"的负担,凭空从哪冒出来的?如果 load average 数的不是 CPU——那它到底在【数什么】?这件事逼着我把 load average 的真实定义、它和 CPU 使用率为什么是两回事、进程的 D 状态(不可中断睡眠)到底是什么,还有"负载高"这个说法背后真正的含义,彻底理清了。本文复盘这次实战。

问题背景

环境:CentOS 7,一台 8 核服务器,挂载了一个 NFS 网络盘
事故现象:
- 监控报警:load average 飙到 50 多,还在涨(8 核机)
- ★ top 一看,CPU 却 95% 空闲,根本没进程烧 CPU
- 负载极高 + CPU 极闲,自相矛盾

现场排查:
# 1. ★ 看 load average —— 确实高得离谱
$ uptime
#  15:42:01 up 60 days,  load average: 51.2, 48.7, 30.5
#                                      ^^^^ ★ 1 分钟负载 51

# 2. ★ 看 CPU —— 却几乎全空闲
$ top
# %Cpu(s):  2.1 us,  2.3 sy,  0.0 ni, 95.3 id, ...
#                                     ^^^^ ★★ 95% 空闲

# 3. ★★ 关键:按进程状态统计,看 D 状态有多少
$ ps -eo state | sort | uniq -c
#     310 S
#       2 R
#      48 D                              # ★★ 48 个 D 状态进程!

# 4. ★ 把这些 D 状态进程列出来
$ ps -eo pid,state,cmd | awk '$2=="D"'
# 12001 D  /usr/bin/php /app/report.php
# 12002 D  cp /nfs/data/big.log /local/
# 12003 D  ls /nfs/data
# ... ★ 全都在访问 /nfs/... 那个 NFS 网络盘

# 5. ★★ 看一个 D 状态进程,到底卡在哪个内核调用上
$ cat /proc/12001/wchan ; echo
# nfs_wait_on_request                    # ★★ 卡在等 NFS 响应

# 6. ★ NFS 服务端 / 网络是不是挂了
$ df -h /nfs/data
# (命令本身也卡住,半天不返回) ★ NFS 挂死了

根因(后来想清楚的):
1. ★ load average,它【不是】"CPU 使用率"。它数的是
   系统里【处于"可运行 R"+"不可中断睡眠 D"状态】
   的进程的平均数量。
2. ★ R 状态:正在用 CPU、或在排队等 CPU 的进程。
3. ★ D 状态:★"不可中断睡眠"—— 进程在等一件【内核
   层面、不能被打断】的事,最典型的就是【等磁盘/
   网络 IO 的结果】。它【不占 CPU】,但它【在 load
   里被算一份】。
4. ★ 本文那个 NFS 网络盘,服务端 / 网络挂了。所有
   访问 /nfs/... 的进程(cp、ls、php...),发出 IO
   请求后,就【死等 NFS 的响应】—— 一直等不到,
   一直卡在 D 状态。
5. ★ 这 48 个进程,全部 D 状态,全部不占 CPU,但
   全部被 load average 算进去 -> load 飙到 50。
6. ★ 所以 CPU 空闲(它们确实没在用 CPU)和 load
   高(它们确实"卡着、是系统的负担")—— 一点都
   不矛盾,因为这俩量,本来就在数不同的东西。
真相:load average 数的是"忙 + 堵"的进程总数,
不只是"忙"。CPU 闲 + load 高 = 系统在大面积"堵"。

修复 1:load average 到底是什么——它不是 CPU 使用率

# === ★ 先扭转最大的误解:load ≠ CPU 使用率 ===

# === ★ 看 load average 的三个数 ===
$ uptime
#  15:42:01 up 60 days,  load average: 51.2, 48.7, 30.5
# ★ 这三个数,分别是【过去 1 分钟、5 分钟、15 分钟】
#   的平均负载。
# ★ 看趋势:1 分钟 > 5 分钟 > 15 分钟 -> 负载在
#   【上升】(本文就是);反过来则在下降。

# === ★★ load average 的真实定义 ===
# ★ 几乎所有人都以为:load = "CPU 有多忙"、"CPU
#   使用率"。★ 这个理解,是错的。
# ★ Linux 的 load average,它统计的是:★ 在某个
#   时刻,系统里处于以下【两种状态】的进程的【数量】,
#   再做时间上的平均:
#  - ① R 状态:Running / Runnable —— 正在 CPU 上跑,
#    或者已经就绪、正排队等着上 CPU 的进程。
#  - ② ★ D 状态:Uninterruptible Sleep —— "不可
#    中断睡眠",通常是在等 IO 等内核操作的进程。
# ★ ★ load = 数 (R 的进程 + D 的进程) 的平均数量。

# === ★ 关键:R 和 D,是两种完全不同的"负担" ===
# ★ R 状态的进程:它们是"想用 CPU"的。如果 R 进程
#   数 > CPU 核数,就有进程在排队等 CPU -> 这是
#   【CPU 不够用】导致的负载。
# ★ ★ D 状态的进程:它们【根本不想用 CPU】,它们
#   在等 IO。但它们【卡着】、是系统的累赘 —— 所以
#   也被算进 load。这是【IO 阻塞】导致的负载。
# ★ ★★ 所以 load 高,可能是 CPU 挤,也可能是 IO 堵,
#   ★ 两种病,同一个症状(load 高)。

# === ★ 这就解释了本文的"矛盾" ===
# ★ CPU 95% 空闲 —— 因为系统里几乎没有 R 状态的
#   进程在抢 CPU。这没错。
# ★ load 飙到 50 —— 因为有 48 个 D 状态的进程,
#   卡在那等 NFS。它们不占 CPU,但每一个都给 load
#   贡献了"1"。
# ★ ★ CPU 闲 + load 高,不矛盾。它俩量的是两件事:
#   一个量"CPU 忙不忙",一个量"有多少进程忙着 +
#   堵着"。本文是 load 里【堵】的那部分爆了。

# === ★ 怎么判断 load 高,到底高在 CPU 还是 IO ===
$ top
# ★ 看 %Cpu(s) 那行:
#  - us / sy 很高,id 很低  -> load 高是【CPU】挤的;
#  - ★ id 很高(CPU 闲),但 wa(iowait)高、或
#    D 状态进程多       -> load 高是【IO】堵的(本文)。
# ★ wa(iowait)= CPU 闲着、但在等 IO 完成的时间占比。
#   wa 高,是 IO 成为瓶颈的强信号。

# === 认知 ===
# ★ load average【不是】CPU 使用率。它统计的是系统里
#   处于 R(正在跑/排队等 CPU)和 ★ D(不可中断睡眠,
#   通常在等 IO)两种状态的进程【数量】的时间平均。
#   所以 load 高有两种完全不同的病因:① R 进程多 ->
#   CPU 不够用;② ★ D 进程多 -> IO 阻塞。★ CPU 空闲
#   + load 高一点不矛盾 —— 那是 load 里"被 IO 堵住"
#   的进程在撑着。看 top 的 us/sy/id/wa 区分是哪种。

修复 2:进程的 D 状态——不可中断睡眠到底是什么

# === ★ 把 D 状态这个根因,彻底讲透 ===

# === ★ 先回顾进程的几种状态(ps 的 STAT 列)===
# ★ R  运行 / 可运行 —— 在用 CPU 或排队等 CPU。
# ★ S  可中断睡眠 —— 在等某件事(等网络、等输入、
#      等定时器),但这种睡眠【可以被信号唤醒/打断】。
#      系统里绝大多数进程平时都是 S。
# ★ ★ D  不可中断睡眠(Uninterruptible Sleep)——
#      也在睡、在等,但这种等待【不可被打断】。
# ★ Z  僵尸;T  停止。

# === ★ D 状态:为什么"不可中断" ===
# ★ 一个进程,当它向内核发起一个【底层 IO 操作】
#   (比如:从磁盘读一个块、向 NFS 写数据),内核
#   会让它进入 D 状态,睡下去,等这个 IO 完成。
# ★ 为什么这种等待要设成"不可中断"?因为这个 IO
#   操作【已经交给硬件 / 驱动在进行中】了。如果此刻
#   允许一个信号(比如 kill)把进程打断、把它拽走,
#   那么这个【进行到一半的 IO】,它的数据缓冲区、
#   内核状态,就可能处于一个【不一致、被损坏】的
#   中间态。
# ★ ★ 所以内核的设计是:进程一旦进入 D 状态等 IO,
#   就【必须老老实实等到这个 IO 有结果】(成功或
#   失败),期间【任何信号都不能动它】—— 这是为了
#   保护数据和内核状态的【完整性】。

# === ★★ 于是一个惊人的推论:D 状态的进程,kill -9 也杀不掉 ===
# ★ kill -9(SIGKILL)是最强的信号。但"不可中断"
#   的字面意思就是:★ 连 SIGKILL 都中断不了它。
# ★ 一个进程卡在 D 状态,你 kill -9 它 —— 信号会
#   【挂起来排队】,但进程【不会立刻死】。它必须
#   等那个 IO【有了结果】,从 D 状态出来,内核才会
#   在那一刻把排队的 SIGKILL 作用上去。
# ★ ★ 所以:如果那个 IO 永远不会有结果(比如 NFS
#   服务端彻底挂了、磁盘彻底坏了)—— 这个 D 状态
#   进程,就【永远卡着,永远 kill 不掉】。

# === ★ D 状态正常 vs 不正常 ===
# ★ ★ 短暂的 D 状态是【完全正常】的!任何进程读写
#   磁盘,都会瞬间经过 D 态 —— 这是 IO 的必经之路,
#   你平时 ps 偶尔瞥见一两个 D,不用慌。
# ★ ★ 不正常的是:【大量】进程【长时间】卡在 D
#   状态出不来。这说明它们等的那个 IO,迟迟没有
#   结果 —— 背后是【IO 子系统出了问题】(盘坏了、
#   网络存储挂了、IO 极度拥塞)。本文就是这种。

# === ★ 验证:看 D 状态进程数和持续时间 ===
$ ps -eo state | sort | uniq -c        # 数各状态进程
$ for i in 1 2 3; do ps -eo state | grep -c '^D'; sleep 2; done
# ★ 如果连续几次,D 的数量都很高且不降 -> 它们是
#   【真卡住了】,不是一闪而过的正常 IO。

# === 认知 ===
# ★ D 状态 = 不可中断睡眠:进程发起底层 IO 后,内核让
#   它睡下等 IO 完成,且这等待【不可被任何信号打断】
#   —— 因为 IO 在硬件层进行中,中途打断会损坏数据/
#   内核状态。★ 推论:D 状态进程连 kill -9 都杀不掉,
#   信号只能排队等它从 D 出来;若 IO 永远无结果,它
#   就永远卡死。短暂 D 是读写磁盘的正常必经态,★ 大量
#   进程长期卡 D 才是病 —— 说明 IO 子系统出了问题。

修复 3:找出 D 状态进程,看它们到底卡在哪

# === ★ load 高 + CPU 闲,锁定是 D 状态在作祟,接下来定位 ===

# === ★ 第一步:把所有 D 状态进程揪出来 ===
$ ps -eo pid,ppid,state,cmd | awk '$3=="D"'
# 12001 8000 D  /usr/bin/php /app/report.php
# 12002 8500 D  cp /nfs/data/big.log /local/
# 12003 9000 D  ls /nfs/data
# ★ -L 还能看到线程级的 D 状态:
$ ps -eLo pid,lwp,state,cmd | awk '$3=="D"'

# === ★ 第二步:★★ 看每个 D 进程,卡在哪个内核函数上 ===
# ★ /proc/PID/wchan —— 进程当前睡在哪个内核函数里:
$ cat /proc/12001/wchan ; echo
# nfs_wait_on_request          # ★ 卡在等 NFS 请求返回
# ★ 或者一次性看所有 D 进程卡在哪:
$ ps -eo pid,state,wchan:32,cmd | awk '$2=="D"'
# 12001 D nfs_wait_on_request      /usr/bin/php ...
# 12002 D nfs_wait_on_request      cp /nfs/...
# ★★ wchan 全是 nfs_* -> 真相大白:全卡在 NFS 上。

# === ★ 第三步:看进程的内核调用栈,卡点更精确 ===
$ cat /proc/12001/stack
# [<0>] nfs_wait_on_request+0x...
# [<0>] nfs_updatepage+0x...
# [<0>] ...
# ★ stack 把这个进程在内核里的整条调用链列出来,
#   一眼看出它陷在哪个子系统(nfs / ext4 / block...)。

# === ★ 第四步:wchan 常见值 -> 对应什么问题 ===
# ★ nfs_*           -> NFS 网络存储问题(服务端挂、网络断)
# ★ io_schedule     -> 在等通用块设备 IO(本地磁盘)
# ★ wait_on_page_*  -> 在等某个内存页(常和 IO 相关)
# ★ ext4_* / xfs_*  -> 文件系统层卡住
# ★ 看到 nfs_ 开头,就去查 NFS;看到 io_schedule,
#   就去查本地磁盘是不是慢了 / 坏了。

# === ★ 第五步:用 IO 工具,确认 IO 子系统的状态 ===
$ iostat -x 2 3
# Device  ...  %util  await  ...
# vda     ...  100.0  5000.0      # ★ %util 100% + await 极高 = 盘忙死/坏了
# ★ %util 接近 100% = 这块盘一刻不停在忙;
# ★ await 极高(正常几毫秒,这里几千毫秒)= 每个
#   IO 请求要等极久 -> 盘扛不住,或盘有问题。
$ dmesg -T | tail -50
# ★ dmesg 里若有 "I/O error"、"nfs: server ... not
#   responding" 之类 -> 硬件 / 网络存储的硬故障实锤。

# === 认知 ===
# ★ 定位 D 状态:① ps -eo pid,state,cmd 筛 state=D 把
#   它们全列出;② ★★ cat /proc/PID/wchan 或 ps 带
#   wchan 列,看每个 D 进程卡在哪个内核函数 —— nfs_*
#   指向 NFS、io_schedule 指向本地块设备、ext4_/xfs_
#   指向文件系统;③ cat /proc/PID/stack 看完整内核
#   调用栈;④ iostat -x 看 %util 和 await 确认盘的
#   状态,dmesg 看有没有 IO error / NFS 超时的硬报错。

修复 4:根因定位——D 状态进程卡 IO 的几种常见来源

# === ★ wchan 指向哪,根因就在哪个方向,逐个看 ===

# === ★ 来源 1:NFS / 网络存储挂了(本文的情况)===
# ★ 现象:wchan 是 nfs_*;访问那个挂载点的命令(连
#   df、ls)统统卡死;dmesg 里有 "nfs: server X not
#   responding, still trying"。
$ dmesg -T | grep -i nfs
# nfs: server 10.0.0.50 not responding, still trying  # ★ 实锤
# ★ 根因:NFS 服务端宕了,或到服务端的网络断了。
#   所有访问这个 NFS 的进程,IO 请求发出去石沉大海,
#   全卡 D 状态。
# ★ 处理:
#  - 治本:恢复 NFS 服务端 / 网络;
#  - ★ 挂载时就该用 soft 选项(mount -o soft),
#    这样 NFS 不通时 IO 会【超时返回错误】,而不是
#    无限期 hard 死等 —— hard 挂载是本文卡死的元凶
#    之一;
#  - 应急:NFS 恢复后,卡住的进程多数会自己缓过来;
#    实在僵死的,umount -f -l 强制卸载那个挂载点。

# === ★ 来源 2:本地磁盘坏了 / 极慢 ===
# ★ 现象:wchan 是 io_schedule;iostat 里某块盘
#   %util 100%、await 高到离谱;dmesg 里有
#   "I/O error"、"ata ... error"、坏扇区告警。
$ dmesg -T | grep -iE 'i/o error|ata|medium error'
# ★ 根因:物理磁盘出问题(坏道、SSD 寿命到、RAID
#   降级)。访问它的进程都卡在 io_schedule。
# ★ 处理:smartctl -a /dev/sdX 看硬盘健康;尽快
#   迁移数据、换盘。

# === ★ 来源 3:磁盘空间 / inode 满了,某些 IO 卡住 ===
$ df -h ; df -i
# ★ 空间满或 inode 满时,部分写操作会异常,进程
#   可能卡在文件系统层。先把空间腾出来。

# === ★ 来源 4:IO 被某个进程打满,其他进程排队 ===
# ★ 不是盘坏,是某个进程(如一个失控的备份、一个
#   全表扫描的数据库)在疯狂读写,把磁盘 IO 带宽
#   占满,别的进程的 IO 全在排队 -> 集体 D 状态。
$ iotop -o                    # ★ 看是哪个进程在狂吃 IO
# ★ 找到那个 IO 大户,限流它(ionice)或错峰跑。

# === ★ 怎么收敛:从 wchan 一路推到根 ===
# ★ load 高 + CPU 闲 -> 必是 D 状态多;
# ★ D 状态多 -> 看 wchan 指向哪个子系统;
# ★ nfs_ -> 查网络存储;io_schedule -> 查本地盘
#   (iostat / dmesg / smartctl / iotop);
# ★ ★ 一句话:D 状态进程是"果",IO 子系统的某个
#   故障(网络存储挂 / 盘坏 / IO 打满)才是"因"。
#   别盯着那些 D 进程,去修它们卡着的那个 IO 源。

# === 认知 ===
# ★ D 状态进程卡 IO,根因按 wchan 指向去查:① NFS/
#   网络存储挂了(wchan nfs_*,dmesg 有 server not
#   responding)—— 治本恢复服务端,且 NFS 该用 soft
#   挂载避免 hard 死等;② 本地磁盘坏/极慢(wchan
#   io_schedule,iostat %util 100%、dmesg I/O error)
#   —— smartctl 查健康换盘;③ 磁盘/inode 满;④ IO
#   被某进程打满(iotop 抓 IO 大户)。D 进程是果,
#   IO 子系统故障才是因 —— 去修 IO 源,别盯着 D 进程。

修复 5:为什么 D 状态进程 kill 不掉、怎么处理

# === ★ 面对一堆卡死的 D 状态进程,能做什么、不能做什么 ===

# === ★ 先认清:D 状态进程,你【几乎】没法直接"杀" ===
$ kill -9 12001
$ ps -p 12001 -o stat
# D                            # ★ 杀完它还是 D,纹丝不动
# ★ 前面讲过:D 状态"不可中断",连 SIGKILL 都得排队
#   等它从 D 里出来。你 kill -9 一个卡死的 D 进程,
#   它【不会马上死】—— 信号挂在那,等那个 IO 有结果
#   的瞬间才生效。
# ★ ★ 所以"D 状态进程 kill 不掉",和上一篇"僵尸
#   kill 不掉",是【两种不同】的杀不掉:
#  - 僵尸:已经死了,没有活进程接收信号;
#  - D 状态:还活着,但被"不可中断"保护着,信号
#    只能排队等。

# === ★ 正确的处理思路:不是杀进程,是解决 IO 源 ===
# ★ D 状态进程是【症状】,IO 故障是【病根】。处理
#   顺序永远是:先治病根。
# ★ ★ 一旦那个 IO 源恢复了(NFS 服务端起来了、
#   网络通了、盘的拥塞缓解了):
#  - 那些卡着的 D 进程,等到的 IO 终于有了结果 ->
#    它们【自动从 D 状态出来】;
#  - 出来的那一刻,之前排队的 SIGKILL 立刻生效 ->
#    被你 kill 过的进程,这时才真正死掉;
#  - load average 也会随之【迅速回落】。
# ★ 所以本文的解法:去把 NFS 恢复。NFS 一通,48 个
#   D 进程几秒内全部缓过来,load 从 50 掉回正常。

# === ★ 如果 IO 源【彻底回不来】了怎么办 ===
# ★ 比如 NFS 服务端物理报废、再也起不来。那些 D
#   进程等的东西永远不会有了 —— 它们会永久卡死。
# ★ 这种情况:
#  - 对 NFS:umount -f(强制)/ umount -l(惰性卸载)
#    那个挂载点,有机会让部分进程的 IO 以错误返回、
#    从而解开 D 状态;
$ umount -f /nfs/data
$ umount -l /nfs/data         # -l:先从目录树摘除,引用清零后真卸
#  - 实在解不开的 D 进程,常常【只能重启这台机器】
#    才能彻底清掉。这也是为什么 hard 挂载的 NFS
#    挂死,有时会逼得整机重启 —— 代价很大。

# === ★ 预防:从源头减少"卡死式"的 D 状态 ===
# ★ ① NFS 等网络存储,挂载用 soft + 合理 timeo,
#   别用 hard 无限死等;关键路径慎用网络存储。
# ★ ② 给磁盘 IO 做监控:iostat 的 %util、await,
#   D 状态进程数,都纳入告警。
# ★ ③ 定期看 dmesg / smartctl,坏盘早发现早换。

# === 认知 ===
# ★ D 状态进程"不可中断",kill -9 也只能排队等它从 D
#   出来 —— 这和僵尸的"杀不掉"是两回事(僵尸是已死、
#   D 是被保护着没法打断)。★ 正确处理不是杀进程,是
#   修好 IO 源:IO 源一恢复,D 进程自动解开、排队的
#   SIGKILL 才生效、load 随之回落。若 IO 源彻底回不来,
#   umount -f/-l 强卸,实在解不开常只能重启整机。预防:
#   NFS 用 soft 挂载别 hard 死等,IO 指标纳入监控。

修复 6:load average 与 D 状态排查纪律

# === 这次事故暴露的认知盲区,定几条纪律 ===

# === 1. ★ load average 不是 CPU 使用率,它数的是 R + D 状态进程的平均数量 ===
$ uptime                                  # 看 1/5/15 分钟负载和趋势

# === 2. ★ load 高先分两种:CPU 挤(R 多)还是 IO 堵(D 多)===
$ top                                     # 看 us/sy/id/wa
# id 高 + load 高 -> 不是 CPU 问题,去查 D 状态

# === 3. ★ 按状态统计进程,看 D 状态有多少 ===
$ ps -eo state | sort | uniq -c

# === 4. ★ D 状态是不可中断睡眠,通常在等 IO,大量长期 D 才是病 ===

# === 5. ★ 看 D 进程卡在哪:/proc/PID/wchan 和 /proc/PID/stack ===
$ ps -eo pid,state,wchan:32,cmd | awk '$2=="D"'

# === 6. ★ wchan 是 nfs_ 查网络存储,是 io_schedule 查本地磁盘 ===

# === 7. ★ 用 iostat -x 看 %util 和 await,dmesg 看 IO error / NFS 超时 ===
$ iostat -x 2 3 ; dmesg -T | tail -50

# === 8. ★ D 状态进程 kill -9 也杀不掉,别硬杀,去修 IO 源 ===

# === 9. ★ NFS 用 soft 挂载别用 hard,否则服务端挂了会把进程死死卡住 ===

# === 10. 排查"load 高 CPU 却闲"的步骤链 ===
$ uptime                                  # ① load 多高
$ top                                     # ② CPU 真的闲?wa 高不高
$ ps -eo state|sort|uniq -c               # ③ D 状态多不多
$ ps -eo pid,state,wchan:32,cmd|awk '$2=="D"'  # ④ D 进程卡在哪
$ iostat -x 2 3 ; dmesg -T|tail            # ⑤ IO 子系统什么状态
# 修 IO 源(NFS/磁盘),D 进程自动解开,load 回落

命令速查

需求                        命令
=============================================================
看 load average             uptime  /  top 第一行
看 CPU 忙闲和 iowait        top(看 us/sy/id/wa)
按状态统计进程              ps -eo state | sort | uniq -c
列出所有 D 状态进程         ps -eo pid,state,cmd | awk '$2=="D"'
看 D 进程卡在哪个内核函数   cat /proc/PID/wchan
看 D 进程的内核调用栈       cat /proc/PID/stack
一次看所有 D 进程的 wchan   ps -eo pid,state,wchan:32,cmd|awk '$2=="D"'
看磁盘 IO 利用率和延迟      iostat -x 2 3
看哪个进程在狂吃 IO        iotop -o
看 IO 硬错误 / NFS 超时     dmesg -T | tail -50
看硬盘健康                  smartctl -a /dev/sdX
强制卸载卡死的 NFS          umount -f /挂载点  /  umount -l /挂载点

口诀:load 数的是 R 加 D 不是 CPU 使用率 CPU 闲 load 高就是 IO 堵
      D 状态不可中断 kill -9 也杀不掉 别杀进程 去修它卡着的那个 IO 源

避坑清单

  1. load average 不是 CPU 使用率,它统计的是 R 加 D 两种状态进程数量的时间平均
  2. load 高有两种病因,R 进程多是 CPU 不够用,D 进程多是 IO 阻塞,要先分清
  3. CPU 空闲但 load 很高一点不矛盾,那是 load 里被 IO 堵住的 D 状态进程在撑着
  4. D 状态是不可中断睡眠,进程在等一个不能被打断的底层 IO,期间任何信号都动不了它
  5. D 状态进程 kill -9 也杀不掉,信号只能排队等它从 D 状态出来,IO 永不返回就永久卡死
  6. 短暂的 D 状态是读写磁盘的正常必经态,大量进程长时间卡 D 才说明 IO 子系统出了问题
  7. 看 D 进程卡在哪用 /proc/PID/wchan,nfs 开头查网络存储,io_schedule 查本地磁盘
  8. iostat 的 %util 接近 100 和 await 极高,说明磁盘忙死或有故障,dmesg 看 IO 硬错误
  9. 处理 D 状态不是去杀进程,是修好它卡着的 IO 源,源一恢复 D 进程会自动解开 load 回落
  10. NFS 挂载要用 soft 选项别用 hard,hard 挂载在服务端挂掉时会把进程死死卡在 D 状态

总结

这次"load 飙到 50、CPU 却闲着"的事故,纠正了我一个关于"忙"的、藏得极深的错觉。在我过去的脑子里,load average 和"CPU 使用率",是【同一个东西】的两个名字。负载高,就是 CPU 忙;CPU 忙,负载就高。这两个量,在我心里是焊死在一起、永远同向变动的。所以当我看见 load 是 50、而 CPU 空闲 95%,我的世界观当场就裂了——这就像有人告诉我,一个房间里挤满了 50 个人,可房间里【没有人在动】。挤,和静,怎么可能同时成立?我盯着那两个数字反复看,几乎要怀疑是 top 显示坏了。直到我把 load 的定义一个字一个字抠清楚,我才明白,我错的根子在哪:我一直以为"负载"衡量的是【CPU 累不累】,可它衡量的,其实是【系统里有多少进程,处在"过不去"的状态】。"过不去"有两种:一种是想用 CPU、却在排队等 CPU(R);另一种,是想要一个结果、却在死等 IO(D)。前一种,是"挤";后一种,是"堵"。我那 50 的负载,从头到尾就没有"挤"的成分——它是 48 个进程,齐刷刷堵在通往那个挂死的 NFS 的路上。它们一个个都不动、都不耗 CPU,可它们每一个,都是一辆抛锚在路中间的车。CPU 那条车道是空的,因为根本没车在开;而 load 这个数,数的不是"多少车在开",是"多少车在路上动弹不得"。复盘到最深,我意识到这件事真正点醒我的,是"忙"和"堵"的区别。一个系统看起来"有负担",可以是因为它在拼命干活(忙),也可以是因为它被什么东西卡住了、干不动(堵)。这是两种【病理完全相反】的状态:忙,是能力被用满了,解法是加资源、是优化、是扩容;堵,是某个环节断了、卡死了,解法是去找那个断点、那个卡点,把它疏通。我若没分清这两者,我就会对着一个"堵"的系统,拼命去做"忙"的优化——给它加 CPU、加机器——而那 48 个进程,会照样堵在 NFS 那条死路上,加多少 CPU 都没用,因为它们要的根本不是 CPU。这个教训,我后来发现到处都用得上:一个接口响应慢,是它在做大量计算(忙),还是它在等一个慢查询、等一个超时的下游(堵)?一个队列在堆积,是消费者处理能力不足(忙),还是消费者卡在某个锁、某个外部调用上(堵)?一个线程池被打满,是任务真的多到处理不完(忙),还是每个任务都卡在 IO 上、占着线程不放(堵)?这些问题,表象都是"满了""慢了""负载高了",可"忙"和"堵"指向的根因和解法,南辕北辙。这次最大的收获,是我学会了在看见任何一个"负载/压力"指标飙高时,绝不立刻下"它太忙了"的结论,而是先冷静地拆一刀:这份压力里,有多少是【真在干活】,有多少是【卡着没法干活】?D 状态这三个字母教给我的,不是一个进程状态,是一个朴素却极易被忽略的事实——一个系统最深的麻烦,常常不是它干得太多,而是它正卡在某个地方,一步,都迈不出去。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
Linux教程

手动能跑 crontab 跑出来却是空的:一次 cron 运行环境的复盘

2026-5-21 10:53:28

Linux教程

date 显示的时间差了 8 小时:一次时区、UTC 与时钟同步的复盘

2026-5-21 11:04:15

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索