我们之前说了常见的进程装填,以及着重说明了不可中断状态和僵尸进程

不可中断状态,一般是进程在和硬件进行交互,保护进程数据和硬件一致的

僵尸进程表示进程已经退出,父进程没有回收这个进程的占用资源

之前我们看的进程问题在于

第一是 iowait太高了,导致系统平均负载升高,达到了系统CPU的个数

第二是僵尸进程一直在增加,应用程序没有正确清理子进程的资源

我们来查看iowait升高的问题

iowait升高,需要查询系统的IO情况,那么什么工具可以查询系统的IO情况呢?

常见的是dstat,可以同时查看CPU和IO两种资源的运行情况,进行对比分析?

那么在终端中运行dstat的命令,观察CPU和IO的情况

# 间隔1秒输出10组数据

$ dstat 1 10

You did not select any stats, using -cdngy by default.

–total-cpu-usage– -dsk/total- -net/total- —paging– —system–

usr sys idl wai stl| read  writ| recv  send|  in   out | int   csw

0   0  96   4   0|1219k  408k|   0     0 |   0     0 |  42   885

0   0   2  98   0|  34M    0 | 198B  790B|   0     0 |  42   138

0   0   0 100   0|  34M    0 |  66B  342B|   0     0 |  42   135

0   0  84  16   0|5633k    0 |  66B  342B|   0     0 |  52   177

0   3  39  58   0|  22M    0 |  66B  342B|   0     0 |  43   144

0   0   0 100   0|  34M    0 | 200B  450B|   0     0 |  46   147

0   0   2  98   0|  34M    0 |  66B  342B|   0     0 |  45   134

0   0   0 100   0|  34M    0 |  66B  342B|   0     0 |  39   131

0   0  83  17   0|5633k    0 |  66B  342B|   0     0 |  46   168

0   3  39  59   0|  22M    0 |  66B  342B|   0     0 |  37   134

在dstat中看出,iowait的升高必然伴随着磁盘的读请求,那么我们就需要了解哪些进程在读取磁盘?这个可以利用top命令做到

top命令看到处于D状态的进程

图片

PID分别是4344和4345

我们查看进程的磁盘读写情况,使用对应的pidstat命令

在对应的输出中 kb_rd代表每秒读的KB数,kB_wr代表每秒写的KB数,iodelay代表IO的延迟,都是0,代表没有任何的读写,这说明4344不会导致iowait升高

那么同理,4345也没有任何问题

那么怎么知道是哪个进程在进行磁盘读写呢?我们继续使用pidstat,观察所有进程的IO使用

对应的pidstat命令如下

图片

看出来,的确是app进程在进行磁盘读,而且每秒的读数据有32MB,那么看来就是app的问题,但是app进程在执行什么io操作呢?

因为io操作涉及到内核态的调用,所以我们的重点就是找出app进程的系统调用了

于是我们需要追踪进程系统调用,常见的就是strace,我们从pidstat中拿到了进程的PID号

strace -p 6082

这时候会发现 strace命令居然失败了,爆出的错误是没有权限,那么所有的操作都是以root用户运行的,为何没有权限呢?

对于这种情况,我们先检查一下进程状态是否正常

ps -ef |grep 6082

会发现已经变成了Z状态,也就是僵尸进程,僵尸进程是已经退出的进程,所以没法进行分析

那么我们只能考虑使用基于事件记录的动态追踪工具了

perf record运行获取报告

然后利用report进行解读

我们找到关注的app进程,按回车展开调用栈,得到如下的调用关系图

图片

其中app的确调用了sys_read进行读取数据,从new_sync_read和blkdev_direct_IO看出

进程正在进行直接读,绕过了系统缓存,每个读请求都从磁盘直接读,解释了观察到的iowait升高了

看来罪魁祸首是app内部的直接IO

那么下面的问题就很简单了,我们从代码层面分析,发现其果然出现了直接读请求,查看源码文件,发现其使用了O_DIRECT选项打开了磁盘,绕过了系统缓存,直接对磁盘进行读写

open(disk, O_RDONLY|O_DIRECT|O_LARGEFILE, 0755)

直接进行读写磁盘,对于IO敏感型的应用是很友好的,可以在应用中,直接控制磁盘的读写,但是对于一般应用,建议使用缓存来优化磁盘IO

那么重新打包运行后

用top看一下,基本没有出现iowait的问题了

图片

不过,解决了iowait的问题

我们还需要处理一下僵尸进程的问题,僵尸进程是父进程没有回收子进程资源导致的,要解决的话,我们需要找到父进程,然后在父进程中解决

父进程的找法,需要我们运行pstree命令

pstree -aps 3084

寻找执行进程的父进程

发现对应的父进程是app进程

这说明仍然要从app应用程序入手,查看子进程结束的处理是否正确,是否没有注册SIGCHLD信号函数

没有合理的调用wait() 或者 waitpid()

查看对应的iowait后的源码,找到创建子进程和结束子进程的地方

图片

发现是循环语句的问题,虽然调用了wait()函数,但是错误的将wait()放在了for循环外面,wait函数没有被调用到

然后进行修复后重新检查

发现僵尸进程已经不存在了,iowait也是0,问题解决了

那么本次总结一下

我们使用了一个多进程的案例,分析了系统iowait升高的情况

对于iowait升高的情况,需要先试用dstat pidstat等工具,确定是不是磁盘自身问题

然后进行判断等待IO进程问题,使用ps查找到D状态进程,多为可疑进程,,但是这次又存在IO操作后,进程变为僵尸进程的问题,所以不能直接用strace分析这个进程系统调用

在之后,使用perf工具,分析对应的CPU时钟事件,发现是直接IO的问题

然后使用pstree找出父进程,检查 wait() waitpid()的调用,或者SIGCHLD的信号处理即可

发表评论

邮箱地址不会被公开。 必填项已用*标注