iowait的CPU使用率升高-大量不可中断进程和僵尸进程

画板

进程的不可中断状态是系统的一种保护机制,可以 保证硬件的交互过程不被意外打断。所以,短时间的不可中断状态是很正常的。但是,当进程长时间都处于不可中断状态时,进程很可能因为得不到硬件的响应,而长时间处于不可中断状态。

从 ps 或者 top 命令的输出中 D 状态,也就是不可中断状态 (Uninterruptible Sleep)。

等待 I/O 的 CPU 使用率(以下简称为 iowait)升高,也是最常见的一个服务器性能问题。

Linux 进程状态查看--top 的 S 列

top的输出里面,S 列(也就是 Status 列)表示进程的状态。

  • R 是 Running 或 Runnable 的缩写,表示进程在 CPU 的就绪队列中,正在运行或者正 在等待运行。
  • D 是 Disk Sleep 的缩写,也就是不可中断状态睡眠****(Uninterruptible Sleep)
    • 不可中断状态,表示进程正在跟硬件交互,为了保护进程数据和硬件的一致性,系统不允 许其他进程或中断打断这个进程。进程长时间处于不可中断状态,通常表示系统有 I/O 性能问题。
    • 保证进程数据与硬 件状态一致,并且正常情况下,不可中断状态在很短时间内就会结束。所以,短时的不可中 断状态进程,我们一般可以忽略。但如果系统或硬件发生了故障,进程可能会在不可中断状态保持很久,甚至导致系统中出现 大量不可中断进程。
  • Z 是 Zombie 的缩写, 它 表示僵尸进程也就是进程实际上已经结束了,但是父进程还没有回收它的资源(比如进 程的描述符、PID 等)。
    • 用尽PID,导致无法创建
    • 正常情况下,当一个进程创建了子进程 后,它应该通过系统调用 wait() 或者 waitpid() 等待子进程结束,回收子进程的资源;而 子进程在结束时,会向它的父进程发送 SIGCHLD 信号,所以,父进程还可以注册 SIGCHLD 信号的处理函数,异步回收资源。
    • 如果父进程没这么做,或是子进程执行太快,父进程还没来得及处理子进程状态,子进程就 已经提前退出,那这时的子进程就会变成僵尸进程。换句话说,父亲应该一直对儿子负责, 善始善终,如果不作为或者跟不上,都会导致“问题少年”的出现。
    • 通常,僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡;或者在父进程退 出后,由 init 进程回收后也会消亡。
    • 一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会一直处于僵尸状 态。大量的僵尸进程会用尽 PID 进程号,导致新进程不能创建,所以这种情况一定要避 免。
  • S 是 Interruptible Sleep 的缩写,也就是可中断状态睡眠,表示进程因为等待某个事件 而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态。
  • I 是 Idle 的缩写,也就是空闲状态,用在不可中断睡眠的内核线程上。硬件交互导致的不可中断进程用 D 表示,但对某些内核线程来说,它们有可能实际上并没有任何负载,用 Idle 正是为了区分这种情况。要注意,D 状态的进程会导致平均负载升高, I 状态的进程却不会。
  • T 或者 t,也就是 Stopped 或 Traced 的缩写,表示进程处于暂停或者跟踪状 态。向一个进程发送 SIGSTOP 信号,它就会因响应这个信号变成暂停状态(Stopped);再向 它发送 SIGCONT 信号,进程又会恢复运行(如果进程是终端里直接启动的,则需要你用 fg 命令,恢复到前台运行)。
    • 用调试器(如 gdb)调试一个进程时,在使用断点中断进程后,进程就会变成跟踪 状态,这其实也是一种特殊的暂停状态,只不过你可以用调试器来跟踪并按需要控制进程的 运行。
  • X,也就是 Dead 的缩写,表示进程已经消亡,所以你不会在 top 或者 ps 命令 中看到它。

dstat 同时观察CPU和磁盘IO的占用

吸收了 vmstat、iostat、ifstat 等几种工具的优点, 可以同时观察系统的 CPU、磁盘 I/O、网络以及内存使用情况。

1625557823284-89e712e9-4f20-4631-935b-402f9c96262f.png

上面的wai就是iowait,后面的读写就是磁盘的读写

  • iowait 太高了,导致系统平均负载升高,并且已经达到了系统 CPU 的个数。
  • 僵尸进程在不断增多,看起来是应用程序没有正确清理子进程的资源。

解决iowait高--直接读写磁盘了

碰到 iowait 升高时,需要先用 dstat、pidstat 等工具,确认是不是磁盘 I/O 的问 题,然后再找是哪些进程导致了 I/O。

等待 I/O 的进程一般是不可中断状态,所以用 ps 命令找到的 D 状态(即不可中断状态) 的进程,多为可疑进程。但这个案例中,在 I/O 操作后,进程又变成了僵尸进程,所以不 能用 strace 直接分析这个进程的系统调用。

这种情况下,我们用了 perf 工具,来分析系统的 CPU 时钟事件,最终发现是直接 I/O 导 致的问题。这时,再检查源码中对应位置的问题,就很轻松了

在终端中运行 dstat 命令,观察 CPU 和 I/O 的使用情况:

间隔 1 秒输出 10 组数据

  1. dstat 1 10
  2. 从 dstat 的输出,我们可以看到,每当 CPU 的 iowait 升高(wai)时,磁盘的读请求(read)都 会很大。这说明 iowait 的升高跟磁盘的读请求有关,很可能就是磁盘读导致的。
  3. 运行 top 命令,观察 D 状态的进程: 从 top 的输出找到 D 状态进程的 PID,分别是 4344 和 4345。
  4. 要查看某一个进 程的资源使用情况,pidstat -d -p 4344 1 3 -d 展示 I/O 统计数据,-p 指定进程号,间隔 1 秒输出 3 组数据,没有读写速率
  5. 是哪个进程在进行磁盘读写呢? 继续使用 pidstat,但这次去掉进 程号,干脆就来观察所有进程的 I/O 使用情况。是app,重点就是找出 app 进程的系统调用了。
  6. strace 正是最常用的跟踪进程系统调用的工具。所以,我们从 pidstat 的输出中拿到进程 的 PID 号,比如 6082,然后在终端中运行 strace 命令,并用 -p 参数指定 PID 号:strace -p 6082,失败,再去查看发现已经变成僵尸进程**ps aux | grep 6082**
  7. 基于事件记录的动态追踪工具:perf record -g ,持续一会儿(例如 15 秒),然后按 Ctrl+C 退出,再运行 perf report 查看报告:发现调用的地方直接操作磁盘,没缓存
  8. 加上缓存,iowait 高 的问题解决

解决僵尸进程

僵尸进程的问题相对容易排查,使用 pstree 找出父进程后,去查看父进程的代码,检查 wait() / waitpid() 的调用,或是 SIGCHLD 信号处理函数的注册就行了。

既然僵尸进程是因为父进程没有回收子进程的资源 而出现的,那么,要解决掉它们,就要找到它们的根儿,也就是找出父进程,然后在父进程 里解决。

1625557114181-61b1c86f-3469-42d4-afbb-604081c1a849.png

3084 号进程的父进程是 4009,也就是 app 应用。查看 app 应用程序的代码,看看子进程结束的处理是否正确,比如有没有 调用 wait() 或 waitpid() ,抑或是,有没有注册 SIGCHLD 信号的处理函数。

附录:进程组和会话

进程组表示一组相互关联的进程,比如每个子进程都是父进程所在组的成员;

而会话是指共享同一个控制终端的一个或多个进程组。

通过 SSH 登录服务器,就会打开一个控制终端(TTY),这个控制终端就对应 一个会话。而我们在终端中运行的命令以及它们的子进程,就构成了一个个的进程组,其 中,在后台运行的命令,构成后台进程组;在前台运行的命令,构成前台进程组。

posted on 2025-10-12 21:41  chuchengzhi  阅读(33)  评论(0)    收藏  举报

导航

杭州技术博主,专注分享云计算领域实战经验、技术教程与行业洞察, 打造聚焦云计算技术的垂直博客,助力开发者快速掌握云服务核心能力。

褚成志 云计算 技术博客