一、 进程状态

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 使用情况(观察久一点)


  1. # -d 展示 I/O 统计数据,-p 指定进程号,间隔 1 秒,输出 3 组数据
  2. 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 号:


  1. $ strace -p 6082
  2. strace: attach: ptrace(PTRACE_SEIZE, 6082): Operation not permitted

这儿出现了一个奇怪的错误,strace 命令居然失败了,并且命令报出的错误是没有权限。按理来说,我们所有操作都已经是以 root 用户运行了,为什么还会没有权限呢?

一般遇到这种问题时,我会先检查一下进程的状态是否正常。比如,继续在终端中运行 ps命令,并使用 grep 找出刚才的 6082 号进程:


  1. ps aux | grep 6082
  2. 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 查看报告:


  1. perf record -g
  2. 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 命令:


  1. # -a表示输出命令行选项,p表示PID,s表示指定进程的父进程
  2. 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 信号处理函数的注册就行了
文章知识点与官方知识档案匹配,可进一步学习相关知识
CS入门技能树Linux入门初识Linux32621 人正在系统学习中

[转帖]《Linux性能优化实战》笔记(五)—— 不可中断进程与僵尸进程的更多相关文章

  1. 深挖计算机基础:Linux性能优化学习笔记

    参考极客时间专栏<Linux性能优化实战>学习笔记 一.CPU性能:13讲 Linux性能优化实战学习笔记:第二讲 Linux性能优化实战学习笔记:第三讲 Linux性能优化实战学习笔记: ...

  2. 《Linux 性能优化实战—倪朋飞 》学习笔记 CPU 篇

    平均负载 指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,即平均活跃进程数 可运行状态:正在使用CPU或者正在等待CPU 的进程,也就是我们常用 ps 命令看到的,处于 R 状态 (Run ...

  3. Linux性能优化实战CPU篇之总结(四)

    一.分析CPU瓶颈 1,性能指标 a>CPU使用率 CPU使用率描述了非空闲时间占总CPU时间的百分比,根据CPU上运行任务的不同可以分为:用户CPU.系统CPU.等待I/O CPU.软中断和硬 ...

  4. Linux性能优化实战学习笔记:第五十二讲

    一.上节回顾 上一节,我们一起学习了怎么使用动态追踪来观察应用程序和内核的行为.先简单来回顾一下.所谓动态追踪,就是在系统或者应用程序还在正常运行的时候,通过内核中提供的探针,来动态追踪它们的行为,从 ...

  5. Linux性能优化实战学习笔记:第五十五讲

    一.上节回顾 上一节,我们一起学习了,应用程序监控的基本思路,先简单回顾一下.应用程序的监控,可以分为指标监控和日志监控两大块. 指标监控,主要是对一定时间段内的性能指标进行测量,然后再通过时间序列的 ...

  6. Linux性能优化实战学习笔记:第五十六讲

    一.上节回顾 上一节,我带你一起梳理了,性能问题分析的一般步骤.先带你简单回顾一下. 我们可以从系统资源瓶颈和应用程序瓶颈,这两个角度来分析性能问题的根源. 从系统资源瓶颈的角度来说,USE 法是最为 ...

  7. Linux性能优化实战学习笔记:第五十七讲

    一.上节回顾 上一节,我带你一起梳理了常见的性能优化思路,先简单回顾一下.我们可以从系统和应用程序两个角度,来进行性能优化. 从系统的角度来说,主要是对 CPU.内存.网络.磁盘 I/O 以及内核软件 ...

  8. Linux性能优化实战学习笔记:第五十八讲

    一.上节回顾 专栏更新至今,咱们专栏最后一部分——综合案例模块也要告一段落了.很高兴看到你没有掉队,仍然在积极学习思考.实践操作,并热情地分享你在实际环境中,遇到过的各种性能问题的分析思路以及优化方法 ...

  9. Linux性能优化实战学习笔记:第五十一讲

    一.上节回顾 上一节,我带你一起学习了常见的动态追踪方法.所谓动态追踪,就是在系统或者应用程序正常运行的时候,通过内核中提供的探针,来动态追踪它们的行为,从而辅助排查出性能问题的瓶颈. 使用动态追踪, ...

  10. Linux性能优化实战学习笔记:第四十五讲

    一.上节回顾 专栏更新至今,四大基础模块的最后一个模块——网络篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,热情地留言和互动.还有不少同学分享了在实际生产环境中,碰到各种性能 ...

随机推荐

  1. Spring Boot中设置定时发送邮件任务

    1:浅谈发送邮箱: 邮箱验证是一个很常见的功能了,基本上每个网站都会用的到, java也有专门的jar来处理邮件发送等服务 2:学过javaweb大家都对发送邮箱上不是很陌生了吧 但之前发送邮箱的步骤 ...

  2. 9 个让你的 Python 代码更快的小技巧

    哈喽大家好,我是咸鱼 我们经常听到 "Python 太慢了","Python 性能不行"这样的观点.但是,只要掌握一些编程技巧,就能大幅提升 Python 的运 ...

  3. libGDX游戏开发之NPC敌人事件(六)

    libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm-国内用libgdx比较少,多数情况需要去官网和google找资料,相互学习的可以加我联系方式. ...

  4. Mybatis源码3 CachingExecutor, 二级缓存,缓存的实现

    Mybatis CachingExecutor, 二级缓存,缓存的实现 一丶二级缓存概述 上一章节,我们知道mybaits在构造SqlSession的时候,需要让SqlSession持有一个执行器,如 ...

  5. Python中的cls语法

      在Python中,cls 是一个用于指代类本身的约定性名称,通常用作类方法(class method)中的第一个参数.cls 类似于 self,它是对类的引用,而不是对实例的引用.cls 通常在类 ...

  6. AI推理实践丨多路极致性能目标检测最佳实践设计解密

    摘要:基于CANN的多路极致性能目标检测最佳实践设计解密. 本文分享自华为云社区<基于CANN的AI推理最佳实践丨多路极致性能目标检测应用设计解密>,作者: 昇腾CANN . 当前人工智能 ...

  7. 案例展示自定义C函数的实现过程

    摘要:用户在使用数据库过程中,受限于内置函数的功能,部分业务不易实现时,可以使用自定义C函数实现特殊功能.本文通过两个示例展示自定义C函数的实现过程. 前言 用户在使用数据库过程中,常常受限于内置函数 ...

  8. 万物皆可Cassandra:HUAWEI Tag背后的神仙数据库

    摘要:在GaussDB(for Cassandra)的支撑下,HUAWEI Tag防丢精灵表现出了非常强大的核心竞争力. 本文分享自华为云社区<万物皆可Cassandra--HUAWEI Tag ...

  9. 火山引擎DataLeap背后的支持者 - 工作流编排调度系统FlowX

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 背景介绍 业务场景 在日常工作中,我们时不时需要对某些逻辑进行重复调度,这时我们就需要一个调度系统.根据不同的调度 ...

  10. 从此告别写 SQL!DataLeap 帮你零门槛完成“数据探查”

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 在日常数据处理工作中,产品.运营.研发或数据分析师经常会面临数据量大且混乱.质量参差不齐的问题,需要花费大量时间和 ...