Linux系统下进程的几种状态介绍

嵌入式技术

1330人已加入

描述

文章对 Linux 系统下进程的几种状态进行介绍,并对系统出现大量僵尸进程和不可中断进程的场景进行分析,使用常用的几种工具进行问题分析定位。

1、进程状态

top 和 ps 是最常用的查看进程状态的工具,下面是 top 命令输出的示例,S 列(也就是 Status 列)表示进程的状态。

cpu

上面数据的 S 列可以看到 R、D、S、I 、Z 几个状态,下面对进程的这几种状态进行介绍。

R 状态:R 是 Running 或 Runnable 的缩写,表示进程在 CPU 的就绪队列中,正在运行或者正在等待运行。

D 状态:D 是 Disk Sleep 的缩写,也就是不可中断状态睡眠(Uninterruptible Sleep),一般表示进程正在跟硬件交互,并且交互过程不允许被其他进程或中断打断。

Z 状态:Z 是 Zombie 的缩写,它表示僵尸进程,也就是进程实际上已经结束了,但是父进程还没有回收它的资源(比如进程的描述符、PID 等)。

S 状态:S 是 Interruptible Sleep 的缩写,也就是可中断状态睡眠,表示进程因为等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态。

I 状态:I 是 Idle 的缩写,也就是空闲状态,用在不可中断睡眠的内核线程上。硬件交互导致的不可中断进程用 D 表示,但对某些内核线程来说,它们有可能实际上并没有任何负载,用 Idle 正是为了区分这种情况。要注意,D 状态的进程会导致平均负载升高, I 状态的进程却不会。

进程的状态除了上面的状态之外,还有 T 和 t 状态,这两种状态都表示进程处于停止状态,但使得进程停止的原因有所差异,如下所示。

T 状态:由信号触发的停止状态,比如向一个进程发送 SIGSTOP 信号,它就会因响应这个信号变成暂停状态(Stopped);再向它发送 SIGCONT 信号,进程又会恢复运行(如果进程是终端里直接启动的,则需要你用 fg 命令,恢复到前台运行)。

t 状态:由调试跟踪触发的停止状态,当使用调试器(如 gdb)调试一个进程时,在使用断点中断进程后,进程就会变成跟踪状态,这其实也是一种特殊的暂停状态,只不过可以用调试器来跟踪并按需要控制进程的运行。

2、案例分析

本案例模拟多进程对磁盘进行读取,使用相关的性能分析工具对例程出现的性能问题进行分析,案例源码在文章后面,编译出的可执行程序名称为 app。

2.1 异常信息分析

在 Ubuntu 系统中打开一个终端,执行 app 进程,打开第二个终端,使用 top 命令查看进程运行状态,如下所示。

cpu

对上面图片中的信息进行分析,找出系统可能存在的异常情况。

第一行中过去 15 分钟、5 分钟、1 分钟的平均负载依次升高,说明平均负载正在升高,过去 1 分钟的平均负载已经快接近 CPU 的个数,说明系统可能已经出现了性能瓶颈。

第二行中存在较多的僵尸进程,说明子进程退出时没有被回收。

查看 CPU 信息,用户 CPU 使用率较低,系统 CPU 使用率和 iowait 有所上升。

查看每个进程的信息,当前运行 app 进程的 CPU 使用率为 20% 左右,且这两个进程的状态为 D 状态,说明该进程有可能正在等待 I/O。

对以上分析进行总结,得出以下两点结论。

系统中僵尸进程在不断增加,说明程序没有正确回收子进程的资源。

系统平均负载在升高,CPU 使用率和 iowait 有所上升,说明平均负载的升高可能是 iowait 引起的,iowait 与系统调用有关。

2.2 iowait 分析

根据上面得出的结论,系统平均负载的上升可能是由于 iowait 引起的,这里推荐一个查看系统 I/O 情况的工具,dstat 可以同时查看 CPU 和 I/O 这两种资源的使用情况,便于对比分析,以 1 秒为间隔,连续输出10组数据,如下所示。

cpu

从 dstat 的输出可以看到,当 iowait(wai)高时,磁盘的读请求(read)都会很大。这说明 iowait 的升高跟磁盘的读请求有关,很可能就是磁盘读导致的。

接下来,需要找出读取磁盘的进程,上面 top 的输出中,进程 id 为 6758 和 6759 的进程处于 D 状态,使用 pidstat 查看这两个进程的情况,-d 参数可以显示 I/O 的使用情况,如下所示。

cpu

上图进程 ID 为 6758 和 6759 的进程 kB_rd/s 数据值为 0,说明不是这两个进程在读取磁盘数据,继续使用 pidstat 查看所有进程的 I/O 使用情况,如下所示(以 1 秒为间隔,输出 3 组数据)。

cpu

上图中 app 进程的 kB_rd/s 的值较大,最大达到 211MB/s,说明 iowait 升高就是 app 进程在读取数据导致的。进程分为用户态和内核态,进程想要访问磁盘,就必须使用系统调用,这也是通过 top 查看时,系统 CPU 和 iowait 一起升高的原因,接下来的重点就是找出 app 进程的系统调用。

strace 工具可以查看进程的系统调用栈,指定 pid 查看进程的系统调用栈,如下所示。

cpu

上图操作显示无权限,但是当前终端权限已经为 root,检查一下进程的状态是否正常,如下所示。

cpu

根据图片信息可以看到,当前进程 ID 为 6945 的 app 进程状态为僵尸状态,僵尸进程是已经退出的进程,所以就没法儿继续分析它的系统调用。

之前提到的 perf 工具,也可以查看进程的调用关系,使用"perf record -g"命令记录调用关系,等待 10 秒左右,使用"perf report"命令报告调用关系,展开 app 进程的调用关系,如下所示。

cpu

app 通过系统调用 sys_read() 读取数据,从 new_sync_read 和 blkdev_direct_IO 可以看出,进程正在对磁盘进行直接读,也就是绕过了系统缓存,每个读请求都会从磁盘直接读,这其实就是导致 iowait 升高的原因,查看 app.c 中磁盘的打开方式,如下所示。

cpu

直接读写磁盘,是直接控制磁盘的读写,不借助系统缓存的优化进行操作,这会导致读写性能受到 I/O 瓶颈的限制,一般直接读写只应用于对 I/O 敏感型的场景,比如数据库系统。但在大部分情况下,最好还是通过系统缓存来优化磁盘 I/O,在例程中删除 O_DIRECT 这个选项。

修改后启动 app 进程,然后使用 top 工具查看系统信息,如下所示,当前平均负载、CPU 使用率和 iowait 已经非常低了,说明刚才的改动已经成功修复了 iowait 高导致系统平均负载升高的问题。

cpu

2.3 僵尸进程问题分析

僵尸进程是因为父进程没有回收子进程的资源造成的,因此,要解决僵尸进程的问题,就要找出父进程,然后在父进程里等待回收子进程,可以通过 pstree 找出子进程对应的父进程,如下所示(-a 表示显示命令行选项,p表PID,s表示指定进程的父进程)。

cpu

通过上面图片看到,进程 id 为 3574 进程的父进程是 3571,还是 app 进程,接下来查看 app 应用程序的代码,分析子进程结束的处理是否正确,比如有没有调用 wait() 或 waitpid() ,抑或是,有没有注册 SIGCHLD 信号的处理函数。app 应用程序对子进程的创建和清理代码如下。

cpu

这段代码虽然看起来调用了 wait() 函数等待子进程结束,但却错误地把 wait() 放到了 for 死循环的外面,wait() 函数实际上并没被调用到,把它挪到 for 循环的里面就可以了。修改后,可以使用 top 命令检查一下,发现系统的僵尸进程数量一直为 0。

2.4 例程源码

cpu

cpu

cpu

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分