一、 进程状态

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. 使用openfrp搭建网站[无公网ip]

    使用openfrp搭建网站的理由 免费/低成本 安全 可扩展 使用条件 有一台低功耗准系统/服务器[无公网ip] u盘 网线/waif网卡 屏幕 使用方法 第1步准备服务 低功耗准系统 / 服务器 推 ...

  2. 通过 KernelUtil.dll 劫持 QQ / TIM 客户端 QQClientkey / QQKey 详细教程(附源码)

    前言 由于 QQ 9.7.20 版本后已经不能通过模拟网页快捷登录来截取 QQClientkey / QQKey,估计是针对访问的程序做了限制,然而经过多方面测试,诸多的地区.环境.机器也针对这种获取 ...

  3. Kubernetes Service 中的 external-traffic-policy 是什么?

    [摘要] external-traffic-policy,顾名思义"外部流量策略",那这个配置有什么作用呢?以及external是指什么东西的外部呢,集群.节点.Pod?今天我们就 ...

  4. 文心一言 VS 讯飞星火 VS chatgpt (46)-- 算法导论6.1 4题

    四.假设一个最大堆的所有元素都不相同,那么该堆的最小元素应该位于哪里? 文心一言: 最大堆的最小元素位于堆的根结点.由于最大堆的性质,根结点是堆中所有元素的最大值,因此它也是堆中所有元素的最小值. 讯 ...

  5. 11、层叠布局(Stack、Align、 Positioned)

    Flutter Stack组件 Stack表示堆的意思,我们可以用Stack或者Stack结合Align或者Stack结合 Positiond来实现页面的定位 布局  Alignment(对齐)类是用 ...

  6. 为什么浏览器会提示网站“不安全”?一文读懂https协议与SSL证书

    [摘要] 为什么浏览器会提示网站"不安全"?从浏览器的"不安全"提示来详细了解https与SSL证书.我们打开很多http网站时候,会看到浏览器提示" ...

  7. 云图说|图解云消息服务KooMessage

    摘要:云消息服务(KooMessage)是提供数字化营销新入口,覆盖全行业.全场景.全终端的一站式富媒体消息服务. 本文分享自华为云社区<[开天aPaaS]图解云消息服务KooMessage&g ...

  8. 跟我学ModelArts丨探索ModelArts平台个性化联邦学习API

    摘要:ModelArts提供了一个实现个性化联邦学习的API--pytorch_fedamp_emnist_classification,它主要是让拥有相似数据分布的客户进行更多合作的一个横向联邦学习 ...

  9. 聊聊Java的异常机制问题

    摘要:java异常指在程序运行时可能出现的一些错误,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,中断了正在执行的程序的正常指令流. 本文分享自华为云社区<Ja ...

  10. Asp.net MVC 跨域设置

    .Net Core 跨域 <system.webServer> <httpProtocol> <customHeaders> <add name=" ...