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