[转帖]《Linux性能优化实战》笔记(五)—— 不可中断进程与僵尸进程
一、 进程状态
1. 状态含义
从 ps或者 top 命令的输出中,可以看到处于不同状态的进程

- R:Running 或 Runnable,表示进程在 CPU 的就绪队列中,正在运行或者正在等待运行
- D:Disk Sleep,不可中断状态睡眠(Uninterruptible Sleep)
- S:Interruptible Sleep,可中断状态睡眠,表示进程因为等待某个资源而被系统挂起,等到之后,会被唤醒并进入 R 状态
- I:Idle,空闲状态
- Z:Zombie ,僵尸进程,子进程实际上已经结束了,但是父进程还没有回收它的资源(比如进程的描述符、PID 等)
- T 或t:即 Stopped 或 Traced,表示进程处于暂停或者跟踪状态。
向一个进程发送 SIGSTOP 信号,它就会因响应这个信号变成暂停状态(Stopped),再向它发送 SIGCONT 信号,进程又会恢复运行。当你用调试器(如 gdb)调试一个进程时,在使用断点中断进程后,进程就会变成跟踪状态,这其实也是一种特殊的暂停状态,只不过你可以用调试器来跟踪并按需要控制进程的运行。
- X:Dead,表示进程已经消亡,所以你不会在 top 或者 ps 命令中看到它。
2. 进程组与会话
有时用ps还能看到以下状态的进程

小写的s和+是什么意思呢?不知道也没关系,查一下 man ps 就可以。s 表示这个进程是一个会话的领导进程,而 + 表示前台进程组。这里又出现了两个新概念,进程组和会话,它们用来管理一组相互关联的进程。
- 进程组表示一组相互关联的进程,比如每个子进程都是父进程所在组的成员
- 会话是指共享同一个控制终端的一个或多个进程组。
比如,我们通过 SSH 登录服务器,就会打开一个控制终端(TTY),这个控制终端就对应一个会话。而我们在终端中运行的命令以及它们的子进程,就构成了一个个的进程组,其中,在后台运行的命令,构成后台进程组;在前台运行的命令,构成前台进程组。
二、 不可中断状态
不可中断状态其实是一种保护机制,是为了保证进程数据与硬件状态一致。如果进程在进行IO操作时被随意中断,很有可能出现数据不一致问题。正常情况下,不可中断状态在很短时间内就会结束,短时间的不可中断状态进程一般可以忽略。
但如果系统或硬件发生了故障,或者 iowait 明显升高,进程可能会在不可中断状态保持很久,甚至导致系统中出现大量不可中断进程。这时,你就得注意下,系统是不是出现了 I/O 等性能问题。
三、 僵尸进程
通常,当一个进程创建了子进程后,它应该通过系统调用 wait() 或者 waitpid() 等待子进程结束,回收子进程的资源;而子进程在结束时,会向它的父进程发送 SIGCHLD 信号,所以,父进程还可以注册SIGCHLD 信号的处理函数,异步回收资源。
如果父进程没这么做,或是子进程执行太快,父进程还没来得及处理子进程状态,子进程就已经提前退出,那这时的子进程就会变成僵尸进程。通常,僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡;或者在父进程退出后,由 init 进程回收后也会消亡。
一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会一直处于僵尸状态。大量的僵尸进程会用尽 PID 进程号,导致新进程不能创建,所以这种情况一定要避免。
四、 案例
来看这个top,有发现什么奇怪的地方吗?

- 2个CPU,平均负载为2,说明此时系统已经满载,且根据时间来看有上升的趋势
- 115个僵尸进程
- CPU使用率不高但 iowait 很高,有超过90%
- 有两个app进程状态为D
根据本节及之前的知识可以推断:
- IO负载过高导致了平均负载升高,可能达到了性能瓶颈
- 僵尸进程多,说明有子进程在退出时没被清理
下面分别来看应该如何分析及处理
五、 iowait分析
什么工具可以查询系统的 I/O 情况呢?首先可以使用 dstat ,它可以同时查看 CPU 和 I/O 的使用情况,便于对比分析。

从 dstat 的输出,我们可以看到,每当 iowait 升高(wai)时,磁盘的读请求(read)都会很大。这说明 iowait 的升高跟磁盘的读请求有关,很可能就是磁盘读导致的。
运行 top 命令观察,发现有两个 D 状态的进程,PID 分别是 4344 和 4345。

接下来可以使用pidstat -d 观察指定进程 I/O 使用情况,或者直接观察所有进程的 I/O 使用情况(观察久一点)
-
# -d 展示 I/O 统计数据,-p 指定进程号,间隔 1 秒,输出 3 组数据
-
pidstat -d -p 4344 1 3
kB_rd 表示每秒读的 KB 数, kB_wr 表示每秒写的 KB 数,iodelay 表示I/O 的延迟(单位是时钟周期)。

可以发现,的确是 app 进程在进行磁盘读,并且每秒读的数据有 32 MB,看来就是 app 的问题。不过,app 进程到底在执行啥 I/O 操作呢?这里,我们需要回顾一下进程用户态和内核态的区别。进程想要访问磁盘,就必须使用系统调用,所以接下来,重点就是找出 app 进程的系统调用了。
strace 正是最常用的跟踪进程系统调用的工具。我们从 pidstat 的输出中拿到进程的 PID 号,比如 6082,然后在终端中运行 strace 命令,并用 -p 参数指定 PID 号:
-
$ strace -p 6082
-
strace: attach: ptrace(PTRACE_SEIZE, 6082): Operation not permitted
这儿出现了一个奇怪的错误,strace 命令居然失败了,并且命令报出的错误是没有权限。按理来说,我们所有操作都已经是以 root 用户运行了,为什么还会没有权限呢?
一般遇到这种问题时,我会先检查一下进程的状态是否正常。比如,继续在终端中运行 ps命令,并使用 grep 找出刚才的 6082 号进程:
-
ps aux | grep 6082
-
root 6082 0.0 0.0 0 0 pts/0 Z+ 13:43 0:00 [app] <defunct
果然,进程 6082 已经变成了 Z 状态,也就是僵尸进程。僵尸进程都是已经退出的进程,也就没法继续分析它的系统调用。
到这一步,你应该注意到了,系统 iowait 的问题还在继续,但是 top、pidstat 这类工具已经不能给出更多的信息了。这时,我们就应该求助那些基于事件记录的动态追踪工具了。
你可以用 perf top 看看有没有新发现,或者在终端中运行 perf record,持续一会儿(例如 15 秒),然后按 Ctrl+C 退出,再运行 perf report 查看报告:
-
perf record -g
-
perf report
找到我们关注的 app 进程,按回车键展开调用栈,你就会得到下面这张调用关系图
这个图里的 swapper 是内核中的调度进程,你可以先忽略掉。我们来看其他信息,你可以发现, app 的确在通过系统调用 sys_read() 读取数据。并且从new_sync_read 和 blkdev_direct_IO 能看出,进程正在对磁盘进行直接读,也就是绕过了系统缓存,每个读请求都会从磁盘直接读,这就可以解释我们观察到的 iowait 升高了。
看来,罪魁祸首是 app 内部进行了磁盘的直接 I/O 啊!
我们接下来应该从代码层面分析,究竟是哪里出现了直接读请求。查看源码文件你会发现它果然使用了 O_DIRECT 选项打开磁盘,绕过了系统缓存,直接对磁盘进行读写。在大部分情况下,我们最好还是通过系统缓存来优化磁盘I/O,换句话说,解决方法,删除 O_DIRECT 这个选项就是了。
open(disk, O_RDONLY|O_DIRECT|O_LARGEFILE, 0755)
六、 僵尸进程分析
既然僵尸进程是因为父进程没有回收子进程的资源而出现的,那么,要解决掉它们,就要找到它们的根,也就是找出父进程,然后在父进程里解决。最简单的就是运行 pstree 命令:
-
# -a表示输出命令行选项,p表示PID,s表示指定进程的父进程
-
pstree -aps 3084

你会发现 3084 号进程的父进程是 4009,也就是 app 应用。我们接着查看 app 应用程序的代码,看看子进程结束的处理是否正确,比如有没有调用 wait() 或 waitpid() ,或者有没有注册 SIGCHLD 信号的处理函数。
找到代码中子进程的创建和清理的地方:
循环语句本来就容易出错,你能找到这里的问题吗?这段代码虽然看起来调用了 wait() 函数等待子进程结束,但却错误地把 wait() 放到了 for 死循环的外面,也就是说,wait() 函数实际上并没被调用到,我们把它挪到 for 循环的里面就可以了。
七、 小结
- iowait 高不一定代表 I/O 有性能瓶颈。当系统中只有 I/O 类型的进程在运行时,iowait 也会很高,但实际上,磁盘的读写远没有达到性能瓶颈的程度。因此,碰到 iowait 升高时,需要先用 dstat、pidstat 等工具,确认是不是磁盘 I/O 的问题,然后再找是哪些进程导致了 I/O。
- 等待 I/O 的进程一般是不可中断状态,所以用 ps 命令找到的 D 状态的进程,多为可疑进程。但这个案例中,在 I/O 操作后,进程又变成了僵尸进程,所以不能用 strace 直接分析这个进程的系统调用。这种情况下,我们用了 perf 工具,来分析系统的 CPU 时钟事件,最终发现是直接 I/O 导致的问题。这时,再检查源码中对应位置的问题,就很轻松了。
- 而僵尸进程的问题相对容易排查,使用 pstree 找出父进程后,去查看父进程的代码,检查wait() / waitpid() 的调用,或是 SIGCHLD 信号处理函数的注册就行了
[转帖]《Linux性能优化实战》笔记(五)—— 不可中断进程与僵尸进程的更多相关文章
- 深挖计算机基础:Linux性能优化学习笔记
参考极客时间专栏<Linux性能优化实战>学习笔记 一.CPU性能:13讲 Linux性能优化实战学习笔记:第二讲 Linux性能优化实战学习笔记:第三讲 Linux性能优化实战学习笔记: ...
- 《Linux 性能优化实战—倪朋飞 》学习笔记 CPU 篇
平均负载 指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,即平均活跃进程数 可运行状态:正在使用CPU或者正在等待CPU 的进程,也就是我们常用 ps 命令看到的,处于 R 状态 (Run ...
- Linux性能优化实战CPU篇之总结(四)
一.分析CPU瓶颈 1,性能指标 a>CPU使用率 CPU使用率描述了非空闲时间占总CPU时间的百分比,根据CPU上运行任务的不同可以分为:用户CPU.系统CPU.等待I/O CPU.软中断和硬 ...
- Linux性能优化实战学习笔记:第五十二讲
一.上节回顾 上一节,我们一起学习了怎么使用动态追踪来观察应用程序和内核的行为.先简单来回顾一下.所谓动态追踪,就是在系统或者应用程序还在正常运行的时候,通过内核中提供的探针,来动态追踪它们的行为,从 ...
- Linux性能优化实战学习笔记:第五十五讲
一.上节回顾 上一节,我们一起学习了,应用程序监控的基本思路,先简单回顾一下.应用程序的监控,可以分为指标监控和日志监控两大块. 指标监控,主要是对一定时间段内的性能指标进行测量,然后再通过时间序列的 ...
- Linux性能优化实战学习笔记:第五十六讲
一.上节回顾 上一节,我带你一起梳理了,性能问题分析的一般步骤.先带你简单回顾一下. 我们可以从系统资源瓶颈和应用程序瓶颈,这两个角度来分析性能问题的根源. 从系统资源瓶颈的角度来说,USE 法是最为 ...
- Linux性能优化实战学习笔记:第五十七讲
一.上节回顾 上一节,我带你一起梳理了常见的性能优化思路,先简单回顾一下.我们可以从系统和应用程序两个角度,来进行性能优化. 从系统的角度来说,主要是对 CPU.内存.网络.磁盘 I/O 以及内核软件 ...
- Linux性能优化实战学习笔记:第五十八讲
一.上节回顾 专栏更新至今,咱们专栏最后一部分——综合案例模块也要告一段落了.很高兴看到你没有掉队,仍然在积极学习思考.实践操作,并热情地分享你在实际环境中,遇到过的各种性能问题的分析思路以及优化方法 ...
- Linux性能优化实战学习笔记:第五十一讲
一.上节回顾 上一节,我带你一起学习了常见的动态追踪方法.所谓动态追踪,就是在系统或者应用程序正常运行的时候,通过内核中提供的探针,来动态追踪它们的行为,从而辅助排查出性能问题的瓶颈. 使用动态追踪, ...
- Linux性能优化实战学习笔记:第四十五讲
一.上节回顾 专栏更新至今,四大基础模块的最后一个模块——网络篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,热情地留言和互动.还有不少同学分享了在实际生产环境中,碰到各种性能 ...
随机推荐
- Jenkins汉化配置
登录进入Jenkins首页 输入:本地ip+端口号(localhost:8099) 进入插件管理页面(Manage Jenkins)安装相关插件 搜索:到available栏目搜索:Locale pl ...
- flutter中全局与单页面背景图片(动态图片)
单页面设置背景图片 使用 Container 组件和 decoration 属性: 优点:简单易用,适用于大多数情况下的页面背景设置. 缺点:无法控制背景图片的位置和层级. class MyBook ...
- 一文为你详解Unique SQL原理和应用
摘要:以一定的算法结合解析树中的各结点,计算出来一个整数值,用来唯一标识这一类SQL,这个整数值被称为Unique SQL ID,Unique SQL ID相同的SQL语句属于同一个"Uni ...
- 常见的6种MySQL约束
摘要:一篇文章带你彻底了解MySQL各种约束 MySQL约束 <1> 概念 是一种限制,它是对表的行和列的数据做出约束,确保表中数据的完整性和唯一性. <2> 使用场景 创建表 ...
- Go 1.18 新特性:多模块工作区模式
摘要:在 Go 1.18 推出多模块工作区模式--Multi-Module Workspaces,用以支持模块的多个工作空间,我们来看看到底有什么特别. 本文分享自华为云社区<一起看看 Go 1 ...
- nodejs升级到最新LTS版本方法汇总:linux/mac/window—npm/yum/ssh
nodejs不同版本的差异还是蛮多的,比如obj?.a 在nodejs12是不支持的,必须得升级到14才可以.但是centos yum 默认安装的,或者系统集成的nodejs版本都是很老的.项目上传到 ...
- Axure Axhub Charts 数据编辑、显示
Axhub Charts图表元件库: https://www.axureshop.com/a/100749.html
- 阿里OSS文件访问变成下载
将 ECS 挂载 OSS 多Bucket ,进行文件存储后,发现PDF.图片在浏览器中访问URL,变成了下载,页不是预览. 1. 解决办法,文件类型 application/octet-stream ...
- 100天搞定机器学习|Day59 主成分分析(PCA)原理及使用详解
数学概念 方差:用来衡量随机变量与其数学期望(均值)之间的偏离程度.统计中的方差(样本方差)是各个数据分别与其平均数之差的平方的和的平均数. $$Var(X)=\frac{1}{n}\sum(x_i- ...
- Asp .Net Core 系列:集成 Ocelot+Consul实现网关、服务注册、服务发现
什么是Ocelot? Ocelot是一个开源的ASP.NET Core微服务网关,它提供了API网关所需的所有功能,如路由.认证.限流.监控等. Ocelot是一个简单.灵活且功能强大的API网关,它 ...