之前的专栏讨论了cpu使用率高的问题,根据之前专栏的相关介绍,其实等待I/O的cpu使用率(简称iowait)升高,也是常见的性能问题,本文就讨论下这个问题
进程状态
top工具显示结果说明
- top输出结果R列相关状态说明
- R:running的缩写,表示进程在cpu就绪队列中,正在运行或者正在等待运行
- D:disk sleep缩写,也就是不可中断睡眠,表示进程在与硬件交互,且在交互过程中不允许被其他进程或中断打断
- Z:zombie缩写,僵尸进程,,就是进程结束了,但是其父进程还没有回收他的资源,比如进程描述符,pid等
- S:interruptible sleep缩写,就是可中断睡眠,表示进程因为某个事件被系统挂起,当进程的事件发生时,它会被唤醒并进入R状态
- I:idle的缩写,就是空闲状态,用在不可中断睡眠的内核线程上,硬件交互导致的不可中断进程用D表示,对于某些内核线程来说,他们可能实际并没有任何负载,用idle就是为了区分这种情况。D状态的进程会导致平均负载升高,I状态的进程不会
除了以上5个状态之外,还有下面两个状态
- T:t,stopped或者traced缩写,表示进程处于暂停或跟踪状态。向一个进程发送SIGSTOP信号,该进程会变成暂停状态(stopped),再发送SIGCONT信号,进程恢复运行,如果进程是终端里直接启动,则需要你用fg命令恢复到前台运行;当你用gdb调试一个进程时,再使用断点中断进程后,进程就变成跟踪状态(traced),这也是一种特殊的暂停状态
- X:dead缩写,表示进程已经消亡,不会在top或ps中看到此进程
不可中断状态,是为保证进程数据与硬件状态一致,正常情况下,不可中断状态会在短时间内结束,所以,短时的不可中断状态,算正常现象,但如果系统或硬件发送故障,进程会长时间在不可中断睡眠状态,甚至导致出现大量不可中断睡眠进程,这时就要引起注意
僵尸进程,是多进程应用很容易碰到的问题,正常情况,当一个进程创建了子进程,它应该通过系统调用wait()或waitpid()等待子进程结束,回收子进程资源;子进程结束,会向它的父进程发送SIGCHLD信号,所以,父进程还可以注册SIGCHLD信号处理函数,异步回收资源,如果父进程没这么做,或者子进程执行太快,父进程没来得及处理子进程状态,子进程就已经提前退出,那此时子进程就变成僵尸进程。通常僵尸进程持续时间较短,在父进程回收他的资源后就会消失,或者父进程退出后,由init进程回收后也会消亡,一旦父进程没有处理子进程的终止,还一直处于运行状态,那子进程就会一直处于僵尸状态,大量僵尸进程会用尽pid,导致新进程不能被创建,所以一定要避免
案例分析
环境说明
- 使用docker运行应用,另外需要使用dstat工具
操作分析
# docker run -it -d --privileged --name=app feisky/app:iowait
# ps aux |grep /app
root 4030 0.0 0.0 4512 1620 pts/0 Ss+ 17:18 0:00 /app
Ss+ : S表示可中断睡眠状态,s表示此进程是一个会话领导进程,+表示前台进程组
进程组:表示一组相互关联的进程,比如每个子进程都是父进程所在组成员
会话:共享同一个控制终端的一个或多个进程组,ssh登陆服务器,就会打开一个控制终端,这个终端就对应一个会话,在终端运行的命令及其它们的子进程,就构成一个个进程组,后台运行的命令,构成后台进程组,在前台运行的命令,构成前台进程组
运行top命令,按1,切换显示所以cpu使用情况
top - 17:32:57 up 12:39, 3 users, load average: 2.65, 2.45, 1.63
Tasks: 537 total, 1 running, 96 sleeping, 0 stopped, 348 zombie
%Cpu0 : 0.3 us, 23.4 sy, 0.0 ni, 34.4 id, 41.8 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.3 us, 18.3 sy, 0.0 ni, 42.2 id, 3.8 wa, 0.0 hi, 35.3 si, 0.0 st
KiB Mem : 2017512 total, 503812 free, 496220 used, 1017480 buff/cache
KiB Swap: 2047996 total, 2047472 free, 524 used. 1354576 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
16 root 20 0 0 0 0 S 28.9 0.0 2:08.95 ksoftirqd/1
4452 root 20 0 0 0 0 Z 22.4 0.0 0:00.68 app
4453 root 20 0 0 0 0 Z 21.4 0.0 0:00.65 app
4330 root 20 0 43192 4264 3232 R 1.0 0.2 0:02.76 top
983 root 20 0 191840 11500 10020 S 0.7 0.6 1:01.13 vmtoolsd
32400 root 20 0 0 0 0 I 0.7 0.0 0:10.08 kworker/0:0
8 root 20 0 0 0 0 I 0.3 0.0 0:18.87 rcu_sched
仔细观察top命令的输出结果,有以下几个特点:
load average 这个参数,1分钟,5分钟,15分钟的数据依次减小,说明平均负载正在升高,并且,1分钟内负载超过cpu个数2,说明系统可能出现了性能瓶颈
Tasks这一行,一个在运行,但僵尸进程很多,并且在不断增加,说明子进程退出时,资源没被清理,产生大量僵尸进程
cpu使用率,不高,但是iowait稍高,好像有点不正常
观察是否有处于D状态进程,由于本实验主机采用了ssd硬盘,出现iowait可能性不大,如果用机械硬盘,会有处于D状态进程,这一般是在等待io
问题汇总一下:
1.iowait高,导致平均负载高,甚至超过了cpu个数
2.僵尸进程不断增多,说明没有正确清理子进程资源
问题处理
因为机器配置原因,可能iowait没达到理想效果,故在此仅说一下处理思路问题
1.使用dstat观察cpu和io情况,一般wai高,磁盘read会很大,说明wai高跟磁盘读请求有关
2.使用top观察处于D状态的进程,并找出这些进程的pid
3.使用pidstat -d 1 3 命令查看输出结果,观察一会,一般会找到问题进程
进程要访问磁盘,就要使用系统调用,为找到问题根源,就需要使用strace这种跟踪系统调用的工具
strace -p pid
运行此命令可能会报错,因为问题进程app已经处于Z状态,表示已经退出了,只不过处于僵尸态,故此会报错,但问题追踪还得继续,可以使用perf top ,perf record, perf report命令进行下一步操作,一般会找到问题根源
僵尸进程处理
- 此处说一下处理思路,出现僵尸进程,一般是要找出其父进程,然后在父进程中解决
- 找到父进程,一般使用pstree命令
pstree -a -p -s <pid>
- 找到父进程,一般使用pstree命令
- 查看父进程程序代码里面,对子进程结束的处理是否正确,如有问题,进行更正极客解决
**总结**
- iowait高不代表io有性能瓶颈
- 系统中只有io类型的进程运行时,iowait也会很高,但实际上,磁盘的读写远远没有达到性能瓶颈的程度
- 碰到iowait高,先用dstat,pidstat确认是不是磁盘io问题,,然后找出导致问题出现的进程
- 等待io进程一般是不可中断状态,可以用ps命令找到D状态的进程,此进程为可疑进程,在本案例中,io操作后,进程变成了僵尸进程,所以不能用strace直接分析,这种情况,我们用了perf工具,逐步发现了问题所在
- 僵尸进程的排查,使用pstree找出父进程,去看父进程代码,去寻找问题所在