性能分析(4)- iowait 使用率太高案例

性能分析小案例系列,能够经过下面连接查看哦html

https://www.cnblogs.com/poloyy/category/1814570.html缓存

 

前言

  • 前面两个案例讲的都是上下文切换致使的 CPU 使用率升高
  • 这一篇就来说讲等待 I/O 致使的 CPU 使用率升高的案例

 

进程状态

详解进程状态

http://www.javashuo.com/article/p-mtpekblm-kw.html服务器

 

不可中断状态

  • 当 iowait 升高时,进程极可能由于得不到硬件的响应,而长时间处于不可中断状态
  • 不可中断也是为了保护进程数据和硬件状态一致,而且正常状况下,不可中断状态在很短期内就会结束
  • 因此,短时的不可中断进程,通常能够忽略
  • 但若是系统或硬件发生了故障,进程可能会在不可中断状态保持好久,甚至致使系统中出现大量不可中断进程。这时,就得注意下,系统是否是出现了 I/O 等性能问题

 

僵尸进程

多进程引用很容易碰到的问题app

 

正常状况

  • 一个进程建立了子进程后,它应该经过系统调用 wait() 或 waitpid() 等待子进程结束,回收子进程的资源
  • 而子进程在结束时,会向它的父进程发送 SIGCHLD 信号
  • 因此,父进程还能够注册 SIGCHLD 信号的处理函数,异步回收资源

 

异常状况

  • 若是父进程没有回收资源,或是子进程执行太快,父进程还没来得及处理子进程状态,子进程就已经提早退出,那这时的子进程就会变成僵尸进程
  • 形象比喻:父亲应该一直对儿子负责, 有始有终,若是不做为或者跟不上,都会致使“问题少年”的出现

 

重点

  • 僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡,或者在父进程退出后,由 init 进程回收后也会消亡
  • 一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会一直处于僵尸状态
  • 大量的僵尸进程会用尽 PID 进程号,致使新进程不能建立

 

大量不可中断状态和僵尸状态进程的案例

系统配置

  • Ubuntu 18.04, 2 CPU,2GB 内存
  • 前置条件:已运行案例应用

 

经过 ps 命令查看案例进程

ps aux | grep /app

结果分析

  • 多个 app 进程已启动
  • 状态有 Ss+、D+、R+
  • 小s:表示这个进程是一个会话的领导进程
  • +:表示前台进程组

 

什么是会话和进程组

  • 它们是用来管理一组相互关联的进程
  • 进程组:好比每一个子进程都是父进程所在组的成员
  • 会话:共享同一个控制终端的一个或多个进程组

 

会话和进程组的场景类比

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

 

经过 top 查看系统情况

结果分析

  • 平均负载,过去 1min、5min、15min 的平均负载依次减小,说明平均负载正在升高
  • 而 1min 内的平均负载已经达到系统 CPU 个数,说明系统极可能存在性能瓶颈
  • 115 zombie 说明僵尸进程比较多,并且在不停增长,有子进程在退出时没被清理
  • 用户 CPU 和系统 CPU 都不高,但 iowait 分别是 60.5% 和 94.6%,好像有点儿不正常,致使系统的平均负载升高
  • 有两个处于 D 状态的 app 进程,可能在等待 I/O

 

查看系统的僵尸进程

ps -e -o stat,ppid,pid,cmd | egrep '^[Zz]'ps -ef | grep "defunct"

一堆 app 僵尸进程异步

 

iowait 分析

一提到 iowait 升高,首先会想要查询系统的 I/O 状况函数

 

运行 dstat 命令,观察 CPU 和 I/O 的使用状况

dstat 1 10

  • 当 iowait 升高(wai)时,磁盘的请求(read)都会很大(M)
  • 这说明 iowait 的升高跟磁盘的读请求有关,极可能就是读磁盘致使的

 

找到读磁盘的进程

  • 经过 top 找到 D 状态的两个 app 进程
  • 不可中断状态表明进程在跟硬件进行交互,极可能就是读磁盘

两个 app 进程的 PID 分别是1240七、12406工具

 

经过 pidstat 查看 app 进程的 I/O 状况

pidstat -d -p 12407 1 5
  • -d 展现 I/O 统计数据
  • -p 指定进程号
  • 间隔 1 秒输出 5 组数据

  • kB_rd 表示每秒的 KB 数, kB_wr 表示每秒的 KB 数,iodelay 表示 I/O 的延迟(单位是时钟周期)
  • 它们都是 0,那就表示此时没有任何的读写,说明问题不 是 12407 进程致使的,也并非12406 进程致使的

 

经过 pidstat 查看系统的 I/O 状况

pidstat -d 1 10

  • 能看到其实的确是 app 进程在读,只不过每过几秒都会有新的 app 进程在读【pid 在不断变化】
  • 能够确认,是 app 进程的问题

 

经过 ps 命令查看一直变化的 app 进程状态

前面讲到读磁盘的 app 进程 PID 一直在变化,那么就来看看已经没在读磁盘的进程的进程状态是怎么样的性能

ps aux | grep 15973

  • 这进程已是 Z 状态,就是僵尸进程
  • 僵尸进程都是已经退出的进程, 因此就无法儿继续分析它的系统调用
  • 关于僵尸进程的处理方法,咱们一下子再说,如今仍是继续分析 iowait 的问题

 

经过 perf 录制性能事件

  • 系统 iowait 的问题还在继续,可是 top、pidstat 这类工具已经不能给出更多的信息了
  • 此时能够经过 perf 动态跟踪性能事件
perf record -g

15s 后 ctrl+c 终止录制spa

 

查看报告,分析报告

perf report

  • app 的确在经过系统调用 sys_read() 读取数据
  • 而且从 new_sync_read 和 blkdev_direct_IO 能看出,进程正在对磁盘进行直接读,也就是绕过了系统缓存,每一个读请求都会从磁盘直接读,这就能够解释观察到的 iowait 升高了

 

修复源码以后,经过 top 命令验证

  • iowait 已经很是低了,只有 0.3%
  • 说明修改源码已经成功修复了 iowait 高的问题
  • 不过,仔细观察僵尸进程的数量,会发现,僵尸进程还在不断的增加中

 

处理和分析僵尸进程

  • 僵尸进程是由于父进程没有回收子进程的资源而出现的
  • 解决僵尸进程须要先找出父进程,而后在父进程里解决

 

经过 pstree 找到某个 app 进程的父进程

pstree -aps 51780

51780 进程的父进程是 51688,也就是 app 应用3d

 

经过 ps 查看全部僵尸进程的父进程

ps -e -o stat,ppid,pid,cmd | egrep '^[Zz]'

全部僵尸进程的父进程都是 51688,从而确认 51688 就是僵尸进程的父进程

 

查看 app 应用程序的代码

查看 app 应用程序的代码,看看子进程结束的处理是否正确

  1. 有没有调用 wait() 或 waitpid() 
  2. 或有没有注册 SIGCHLD 信号的处理函数

把 wait() 放到了 for 死循环的外面,也就是说, wait()  函数实际上并没被调用到,把它挪到 for 循环的里面就能够了

 

改完源码,经过 top 验证一下

僵尸进程(Z 状态)没有了, iowait 也是 0,问题终于所有解决了

 

总结

  • 这个案例是由于磁盘 I/O 致使了 iowait 升高
  • 不过,iowait 高并不必定表明 I/O 有性能瓶颈
  • 当系统中只有 I/O 类型的进程在运行时,iowait 也会很高,但实际上,磁盘的读写远没有达到性能瓶颈的程度

 

分析总体思路

  1. 经过 top 查看系统资源状况
  2. 发现平均负载逐渐升高,iowait(wa)比较高,但用户态和内核态 CPU 使用率并不算高
  3. 查看是否有 CPU 使用率偏高的进程,发现有 D 状态的进程,多是在等待 I/O 中
  4. 过一阵子会变成 Z 状态进程,且 CPU 使用率上升,而后会看到 zombie 进程数逐渐增长
  5. 能够获得两个结论:僵尸进程过多,应该是父进程没有清理已经结束的子进程的资源;iowait 的上升导系统平均负载上升
  6. 由于是 iowait 较高,能够经过 dstat 查看系统的 I/O 状况,会发现每次 iowait 升高,读磁盘请求都会很大
  7. 经过 pidstat -d 查看 D 状态进程的 I/O 状况,但发现并无有效信息
  8. 经过 pidstat -d 直接查看系统的 I/O 状况,能够发现不断有新进程在进行读磁盘操做
  9. 经过 ps 命令查看刚刚 D 状态进程当前的进程状态,发现已经变成僵尸进程
  10. 经过 perf record 录制性能事件,而后经过 perf report 查看性能报告,能够发现 app 进程都是直接读磁盘,而不通过系统缓存
  11. 经过 pstree 找到 Z 状态进程的父进程
  12. 经过 ps 命令确认全部僵尸进程的父进程
  13. 找到父进程源代码,检查 wait() / waitpid() 的是否会成功调用,或是 SIGCHLD 信号处理函数的注册就好了
  14. 修改彻底部源码后,从新运行应用,经过 top 验证是否还有 iowait 太高和出现 zombie 进程的状况
相关文章
相关标签/搜索