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

文章对 linux 系统下进程的几种状态进行介绍,并对系统出现大量僵尸进程和不可中断进程的场景进行分析,使用常用的几种工具进行问题分析定位。
1、进程状态
top 和 ps 是最常用的查看进程状态的工具,下面是 top 命令输出的示例,s 列(也就是 status 列)表示进程的状态。
上面数据的 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 命令查看进程运行状态,如下所示。
对上面图片中的信息进行分析,找出系统可能存在的异常情况。
第一行中过去 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组数据,如下所示。
从 dstat 的输出可以看到,当 iowait(wai)高时,磁盘的读请求(read)都会很大。这说明 iowait 的升高跟磁盘的读请求有关,很可能就是磁盘读导致的。
接下来,需要找出读取磁盘的进程,上面 top 的输出中,进程 id 为 6758 和 6759 的进程处于 d 状态,使用 pidstat 查看这两个进程的情况,-d 参数可以显示 i/o 的使用情况,如下所示。
上图进程 id 为 6758 和 6759 的进程 kb_rd/s 数据值为 0,说明不是这两个进程在读取磁盘数据,继续使用 pidstat 查看所有进程的 i/o 使用情况,如下所示(以 1 秒为间隔,输出 3 组数据)。
上图中 app 进程的 kb_rd/s 的值较大,最大达到 211mb/s,说明 iowait 升高就是 app 进程在读取数据导致的。进程分为用户态和内核态,进程想要访问磁盘,就必须使用系统调用,这也是通过 top 查看时,系统 cpu 和 iowait 一起升高的原因,接下来的重点就是找出 app 进程的系统调用。
strace 工具可以查看进程的系统调用栈,指定 pid 查看进程的系统调用栈,如下所示。
上图操作显示无权限,但是当前终端权限已经为 root,检查一下进程的状态是否正常,如下所示。
根据图片信息可以看到,当前进程 id 为 6945 的 app 进程状态为僵尸状态,僵尸进程是已经退出的进程,所以就没法儿继续分析它的系统调用。
之前提到的 perf 工具,也可以查看进程的调用关系,使用perf record -g命令记录调用关系,等待 10 秒左右,使用perf report命令报告调用关系,展开 app 进程的调用关系,如下所示。
app 通过系统调用 sys_read() 读取数据,从 new_sync_read 和 blkdev_direct_io 可以看出,进程正在对磁盘进行直接读,也就是绕过了系统缓存,每个读请求都会从磁盘直接读,这其实就是导致 iowait 升高的原因,查看 app.c 中磁盘的打开方式,如下所示。
直接读写磁盘,是直接控制磁盘的读写,不借助系统缓存的优化进行操作,这会导致读写性能受到 i/o 瓶颈的限制,一般直接读写只应用于对 i/o 敏感型的场景,比如数据库系统。但在大部分情况下,最好还是通过系统缓存来优化磁盘 i/o,在例程中删除 o_direct 这个选项。
修改后启动 app 进程,然后使用 top 工具查看系统信息,如下所示,当前平均负载、cpu 使用率和 iowait 已经非常低了,说明刚才的改动已经成功修复了 iowait 高导致系统平均负载升高的问题。
2.3 僵尸进程问题分析
僵尸进程是因为父进程没有回收子进程的资源造成的,因此,要解决僵尸进程的问题,就要找出父进程,然后在父进程里等待回收子进程,可以通过 pstree 找出子进程对应的父进程,如下所示(-a 表示显示命令行选项,p表pid,s表示指定进程的父进程)。
通过上面图片看到,进程 id 为 3574 进程的父进程是 3571,还是 app 进程,接下来查看 app 应用程序的代码,分析子进程结束的处理是否正确,比如有没有调用 wait() 或 waitpid() ,抑或是,有没有注册 sigchld 信号的处理函数。app 应用程序对子进程的创建和清理代码如下。
这段代码虽然看起来调用了 wait() 函数等待子进程结束,但却错误地把 wait() 放到了 for 死循环的外面,wait() 函数实际上并没被调用到,把它挪到 for 循环的里面就可以了。修改后,可以使用 top 命令检查一下,发现系统的僵尸进程数量一直为 0。
2.4 例程源码

如何避免二极管过载损坏
要想快速充电,必先从充电装置入手
苹果13预计上市时间价格 苹果13什么时候发售
怎样用ClickteamFusion2.5与Arduino等串行设备进行通信
数商云采购管理系统邀请招标实施步骤,助力建筑工程企业采购工作降本增效
Linux系统下进程的几种状态介绍
贸泽电子推出新一期EIT计划 共同探索环境传感器前沿技术和应用
精密全波整流电路的十种电路图
海韵Connect SSR-750FA正式发布 拥有独立线缆接口模块且采用磁吸式设计
降低噪声小妙招:同步开关稳压器
飞兆推出带USB/充电器检测功能的便携应用OVP器件FAN3
冷却塔有什么用_冷却塔应用在什么场所
苹果iPhone手机在中国智能手机市场竞争激烈需要从价格方面下手
明年Mini LED背光或迎来爆发式增长
2010年IT市场10大预测
高等级自动驾驶芯片征程 5是对“数字发动机”的全新升级?
智慧工地巡检机器人有哪些功能
中国半导体产业在新的技术革命下有什么样的发展机会
气密检漏仪在医疗设备行业的应用
美俄反无人机蜂群技术的原理