性能分析4-iowait 使用率过高案例

作者: 小菠萝测试笔记

性能分析小案例系列,可以通过下面链接查看哦 https://www.cnblogs.com/poloyy/category/1814570.html

前言

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

进程状态

详解进程状态 https://www.cnblogs.com/poloyy/p/13413770.html 不可中断状态

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

多进程引用很容易碰到的问题 正常情况

  • 一个进程创建了子进程后,它应该通过系统调用 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”

![](https://static.oomspot.com/image/bost/2021/1896874-20200810132505163-881199828.png)

一堆 app 僵尸进程
 iowait 分析

一提到 iowait 升高,首先会想要查询**系统的 I/O 情况**
 运行 dstat 命令,观察 CPU 和 I/O 的使用情况

dstat 1 10

![](https://static.oomspot.com/image/bost/2021/1896874-20200810133113985-739409582.png)
* 当 iowait 升高(wai)时,磁盘的**读**请求(read)都会很大(M)
* 这说明 iowait 的升高跟磁盘的读请求有关,很可能就是**读磁盘**导致的
 找到读磁盘的进程
* 通过 top 找到 D 状态的两个 app 进程
* 不可中断状态代表进程在跟硬件进行交互,很可能就是读磁盘

![](https://static.oomspot.com/image/bost/2021/1896874-20200810133737049-1683069756.png)

两个 app 进程的 PID 分别是12407、12406
 通过 pidstat 查看 app 进程的 I/O 情况

pidstat -d -p 12407 1 5

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

![](https://static.oomspot.com/image/bost/2021/1896874-20200810134200567-1824338108.png)
* **kB_rd** 表示每秒**读**的 KB 数, **kB_wr** 表示每秒**写**的 KB 数,iodelay 表示 I/O 的延迟(单位是时钟周期)
* 它们都是 0,那就表示此时没有任何的读写,说明问题不 是 12407 进程导致的,也并不是12406 进程导致的
 通过 pidstat 查看系统的 I/O 情况

pidstat -d 1 10

![](https://static.oomspot.com/image/bost/2021/1896874-20200810134649804-1991405866.png)
* 能看到其实的确是 app 进程在读,只不过每过几秒都会有新的 app 进程在读**【pid 在不断变化】**
* 可以确认,是 app 进程的问题
 通过 ps 命令查看一直变化的 app 进程状态

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

ps aux | grep 15973

![](https://static.oomspot.com/image/bost/2021/1896874-20200810135841862-314131450.png)
* 这进程已经是 Z 状态,就是**僵尸进程**了
* 僵尸进程都是已经退出的进程, 所以就没法儿继续分析它的系统调用
* 关于僵尸进程的处理方法,们一会儿再说,现在还是继续分析 iowait 的问题
 通过 perf 录制性能事件
* 系统 iowait 的问题还在继续,但是 top、pidstat 这类工具已经不能给出更多的信息了
* 此时可以通过 **perf** 动态跟踪性能事件

perf record -g

15s 后 ctrl+c 终止录制
 查看报告,分析报告

perf report

![](https://static.oomspot.com/image/bost/2021/1896874-20200810143346433-1117637301.png)
* app 的确在通过系统调用 sys_read() 读取数据
* 并且从 new_sync_read 和 blkdev_direct_IO 能看出,进程正在对磁盘进行直接读,也就是**绕过了系统缓存,每个读请求都会从磁盘直接读**,这就可以解释观察到的 iowait 升高了
 修复源码之后,通过 top 命令验证

![](https://static.oomspot.com/image/bost/2021/1896874-20200810151106773-2079753992.png)
* iowait 已经非常低了,只有 0.3%
* 说明修改源码已经成功修复了 iowait 高的问题
* 不过,仔细观察僵尸进程的数量,会发现,僵尸进程还在不断的增长中
 处理和分析僵尸进程
* 僵尸进程是因为**父进程**没有回收子进程的资源而出现的
* 解决僵尸进程需要先找出父进程,然后在父进程里解决
 通过 pstree 找到某个 app 进程的父进程

pstree -aps 51780

![](https://static.oomspot.com/image/bost/2021/1896874-20200810152040428-104498746.png)
51780 进程的父进程是 51688,也就是 app 应用
 通过 ps 查看所有僵尸进程的父进程

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

![](https://static.oomspot.com/image/bost/2021/1896874-20200810152139837-826373446.png)

所有僵尸进程的父进程都是 51688,从而确认 51688 就是僵尸进程的父进程
 查看 app 应用程序的代码

查看 app 应用程序的代码,看看**子进程结束的处理是否正确**
1. 有没有调用 wait() 或 waitpid()
2. 或有没有注册 SIGCHLD 信号的处理函数

![](https://static.oomspot.com/image/bost/2021/1896874-20200810153122342-987703224.png)

把 wait() 放到了 for 死循环的外面,也就是说, wait() 函数实际上并没被调用到,把它挪到 for 循环的里面就可以了
 改完源码,通过 top 验证一下

![](https://static.oomspot.com/image/bost/2021/1896874-20200810153729935-1410641449.png)

僵尸进程(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 进程的情况  
> 原文创作:小菠萝测试笔记
>
> 原文链接:https://www.cnblogs.com/poloyy/p/13424572.html

## 文章列表
- [性能测试必备知识9-深入理解“软中断”](https://www.oomspot.com/post/xingnengceshibibeizhishi9shenrulijieruanzhongduan)
- [性能测试必备知识8-深入理解“进程状态”](https://www.oomspot.com/post/gnengceshibibeizhishi8shenrulijiejinchengzhuangtai)
- [性能分析5-软中断导致 CPU 使用率过高的案例](https://www.oomspot.com/post/gfenxi5ruanzhongduandaozhicpushiyongluguogaodeanli)
- [性能分析4-iowait 使用率过高案例](https://www.oomspot.com/post/xingnengfenxi4iowaitshiyongluguogaoanli)

更多推荐

更多
  • Pharo敏捷人工智能-第一部分:神经网络
    Apache CN

  • Pharo敏捷人工智能-第二部分:遗传算法
    Apache CN

  • Pharo敏捷人工智能-# 第三部分:神经进化 第三部分:神经进化
    Apache CN

  • 精通Java11-十一、新工具和工具增强功能 技术要求,使用 HTTP 客户端,Java9 之前的 HTTP 客户端,Java11 HTTP 客户端,HTTP 客户端 API 的限制,了解 Javadoc 和 Doclet API,Java9 之前的 Doclet API,API
  • 精通Java11-十三、安全增强功能 技术要求,数据报传输层安全,DTLS 协议版本 1.0,DTLS 协议版本 1.2,Java 中的 DTLS 支持,创建 PKCS12 密钥库,密钥库入门,Java 密钥库JKS,了解密钥库生成器,CallbackHandlerProt
  • 精通Java11-十四、命令行标志 技术要求,统一 JVM 日志记录,命令行选项,装饰,级别,使用 Xlog 输出,标签,编译器控制,编译模式,C1 编译模式,C2 编译模式,分层编译,Java11 中的编译器控制,诊断命令,堆性能分析代理,移除 JHAT,命令行标志参数
  • 精通Java11-十、细粒度栈跟踪 技术要求,Java 栈概述,栈信息的重要性,示例限制调用者,示例–为调用者获取记录器,与StackWalker合作,获取StackWalker的实例,枚举选项,RETAIN_CLASS_REFERENCE,SHOW_REFLECT_FR
  • 精通Java11-十二、并发性增强 技术要求,反应式程序设计,反应式程序设计标准化,Flow API,Flow.Publisher接口,Flow.Subscriber接口,Flow.Subscription接口,Flow.Processor接口,示例实现,额外的并发更新,
  • 精通Java11-十六、未来发展方向 技术要求,JDK 增强提案概述,JEP 候选,JEP326:原始字符串字面值,JEP334:JVM 常量 API,JEP337:RDMA 网络套接字,JEP338:向量 API,JEP339:Edwards 曲线数字签名算法,已提交的
  • 精通Java11-十五、Java 平台的其他增强功能 技术要求,UTF8 支持,ResourceBundle类,嵌套类,字段和构造器,方法,现代 Java 平台的变化,Unicode 支持,java.lang包,java.text包,额外重要事项,Linux/AArch64 端口,多分辨率
  • 近期文章

    更多
    文章目录

      推荐作者

      更多