先记住这个答案
先看 top 的进程列表或 ps -eo state,pid,cmd 找 D 状态进程,D 代表不可中断睡眠,通常等待磁盘或网络 IO。再用 iostat -x 1 看 %util 和 await,若磁盘忙则 IO 是瓶颈。CPU 低是因为任务没在算,而在等 I/O 完成,load 只统计运行和不可中断任务,不会因等待 CPU 而降低。
- load 统计运行与不可中断睡眠任务
- D 状态常等磁盘或网络 IO
- 用 ps 和 iostat 联合定位 IO 瓶颈
load 为何在 CPU 低时依然高
Linux 的 load average 是运行队列中可运行任务和不可中断睡眠任务数量的衰减平均值。可运行任务对应 top 的 R 状态,正在执行或等待 CPU;不可中断睡眠任务即 D 状态,常因等待磁盘、网络或硬件 IO 而进入。这两类任务都计入 load,所以即使 CPU 空闲,只要 D 状态任务堆积,load 也会升高。
当磁盘出现性能问题时,大量进程会阻塞在 uninterruptible sleep,此时 top 的 %Cpu(s) 里 us、sy 等列总和可能很低,因为 CPU 并没有被大量计算占用,空闲 id 却不低。读 man top 的 UPTIME and LOAD Averages 部分可知 load 计算的正是这些状态,因此不能只凭 CPU 百分比判断系统是否繁忙。
具体场景:数据库备份导致 load 飙高
一台 8 核服务器运行 Web 应用,top 显示 load average 为 23.5,但 %Cpu(s) 只有 us 4.2, sy 1.8, id 90.1。任务列表里发现大量 mysqld 和 tar 进程处于 D 状态。此时先执行 ps -eo pid,state,cmd | awk '$2=="D"{print}',确认阻塞进程及数量。
随后用 iostat -x 1 观察,%util 持续 100%,await 超过 500ms,且 w_await 很高。判断是磁盘写 IO 饱和。决策:暂停备份任务或改用低优先级并限制 IO 带宽,例如用 ionice -c2 -n7 降低备份进程的 IO 优先级,或错峰执行。处理后备机,load 和磁盘指标回落,应用响应恢复。
何时不能直接归因于磁盘 IO
D 状态并非全部由块设备 IO 引起。例如 NFS 挂载的网络文件系统在服务器不响应时,进程也会进入不可中断睡眠,此时 iostat 看到的本地磁盘可能空闲。需要使用 cat /proc/<pid>/stack 查看内核调用栈,或使用 perf trace 观察阻塞点,仅靠 iostat 可能误判。
另一种情况是大量短时进程不断 fork,导致 load 计算期间 R 状态任务瞬时高但 CPU 快速交替,top 的刷新间隔可能错过峰值。可缩短采样间隔到 1 秒并用 pidstat -u 1 观察进程级 CPU,若 CPU 利用率平均值仍低,则更支持 IO 或锁等待。排查时若只看到 id 高就下结论磁盘繁忙,容易漏掉进程频繁切换造成的 load 虚高。
容易答错的地方
- load 高就以为必须加 CPU
- 加核数只缓解真实 CPU 竞争。若瓶颈是 IO,增加 CPU 不会降低 load,反而可能因更多任务同时发起 IO 恶化等待。应先用 ps 确认 D 状态占比再决定扩容。
- D 状态一定有罪魁祸首
- D 状态可能是合理的 IO 等待,例如内存页换入换出。当
free显示可用内存严重不足,内核频繁 swap 时,即使iostat磁盘繁忙,根因是内存压力而非纯磁盘损坏。需同时检查vmstat的si、so列。
面试官还会怎么问?
D 状态进程杀不掉,直接 kill -9 会怎样?
D 状态不可中断,kill -9 也会被挂起直到内核完成等待,信号排队但进程不退出。正确做法是排查底层 IO 是否恢复、检查存储或网络,等状态自然结束。
怎么快速确认是某块盘而不是整个 IO 子系统?
用 iostat -x 1 逐盘观察 %util 和 await,高值盘即为热点。若多块盘同时高,考虑链路或控制器。再用 iotop 按进程汇总 IO 排序定位。
load 显示长时间超过核数,但 CPU 仍有余量,可能是什么?
除 IO 外,还有不可中断的锁等待,比如内核或内核模块的互斥锁。可查看 /proc/<pid>/stack 和 ps -o stat,若状态为 D 且 stack 显示等待某个 mutex,需要用 ftrace 或 kgdb 深入内核排查。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。