专栏首页秘籍酷Linux并发(进程的生老病死)

Linux并发(进程的生老病死)

Linux中的进程有生老病死,就跟人一样,我们尤其关注其死,因为进程死后如果不处理,它会变成僵尸!

拓展:

说进程是动态的活动的实体,指的是进程会有很多种运行状态,一会儿睡眠、一会儿暂停、一会儿又继续执行。下图给出Linux进程从被创建(生)到被回收(死)的全部状态,以及这些状态发生转换时的条件:

结合该图,一起理一理进程从生到死的过程:

1,从“蛋生”可以看到,一个进程的诞生,是从其父进程调用fork( )开始的。

2,进程刚被创建出来的时候,处于TASK_RUNNING状态,从图中看到,处于该状态的进程可以是正在进程等待队列中排队,也可以占用CPU正在运行,我们习惯上称前者为“就绪态”,称后者为“执行态”。当进程状态为TASK_RUNNING并且占用CPU时才是真正的运行。

3,刚被创建的进程都处于“就绪”状态,等待系统调度,内核中的函数sched()被称为调度器,他会根据各种参数来选择一个等待的进程去占用CPU。进程占用CPU之后就可以真正运行了,运行时间有个限定,比如20毫秒,这段时间被称为time slice,即“时间片”的概念。

换句话说:进程跟人一样,从来都没有什么平等可言,有贵族就有屌丝,他们要处理的事情有不同的轻重缓急之分。

4,进程处于“执行态”时,可能会由于某些资源的不可得而被置为“睡眠态/挂起态”,比如进程要读取一个管道文件数据而管道为空,或者进程要获得一个锁资源而当前锁不可获取,或者干脆进程自己调用sleep( )来强制自己挂起,这些情况下进程的状态都会变成TASK_INTERRUPIBLE或者TASK_UNINTERRUPIBLE,他们的区别是一般后者跟某些硬件设置相关,在睡眠期间不能响应信号,因此TASK_UNINTERRUPIBLE的状态也被称为深度睡眠,相应地TASK_INTERRUPIBLE期间进程是可以响应信号的。当进程所等待的资源变得可获取时,又会被系统置为TASK_RUNNING状态重新就绪排队。

5,当进程收到SIGSTOP或者SIGTSTP中的其中一个信号时,状态会被置为TASK_STOPPED,此时被称为“暂停态”,该状态下的进程不再参与调度,但系统资源不释放,直到收到SIGCONT信号后被重新置为就绪态。当进程被追踪时(典型情况是被调试器调戏时),收到任何信号状态都会被置为TASK_TRACED,该状态跟暂停态是一样的,一直要等到SIGCONT才会重新参与系统进程调度。

6,运行的进程跟人一样,迟早都会死掉。进程的死亡可以有多种方式,可以是寿终正寝的正常退出,也可以是被异常杀死。比如上图中,在main函数内return或者调用exit(),包括在最后线程调用pthread_exit()都是正常退出,而受到致命信号死掉的情况则是异常死亡,不管怎么死,最后内核都会调用一个叫do_exit()的函数来使得进程的状态变成所谓的僵尸态EXIT_ZOMBIE,单词ZOMBIE对于玩过“植物大战僵尸”的童鞋都不会陌生,这里的“僵尸”指的是进程的PCB(进程控制块)。

为什么一个进程的死掉之后还要把尸体留下呢?因为进程在退出的时候,将其退出信息都封存在他的尸体里面了,比如如果他正常退出,那退出值是多少呢?如果被信号杀死?那么是哪个信号呢?这些“死亡信息”都被一一封存在该进程的PCB当中,好让别人可以清楚地知道:我是怎么死的。

那谁会关心他是怎么死的呢?答案是他的父进程,他的父进程之所以要创建他,很大的原因是要让这个孩子去干某一件事情,现在这个孩子已死,那事情办得如何,孩子是否需要有个交代?但他又死掉了,所以之后将这些“死亡信息”封存在自己的尸体里面,等着父进程去查看,比如父子进程可以约定:如果事情办成了退出值为0,如果权限不足退出值为1,如果内存不够退出值为2等等等等。父进程可以随时查看一个已经死去的孩子的事情究竟办得如何。可以看到,在工业社会中,哪怕是进程间的协作,也充满了契约精神。

7,父进程调用wait()/waitpid()来查看孩子的“死亡信息”,顺便做一件非常重要的事情:将该孩子的状态置为EXIT_DEAD,即死亡态,因为处于这个状态的进程的PCB才能被系统回收。由此可见,父进程应该尽职尽责地及时调用wait()/waitpid(),否则系统会充满越来越多的“僵尸”!

问题是,如何保证父进程一定要及时地调用wait()/waitpid()从而避免僵尸进程泛滥呢?答案是不能,因为父进程也许需要做别的事情没空去帮那些死去的孩子收尸,甚至那些孩子在变成僵尸的时候,他的父进程已经先他而去了!

后一种情况其实比较容易解决:如果一个进程的父进程退出,那么祖先进程init(该进程是系统第一个运行的进程,他的PCB是从内核的启动镜像文件中直接加载的,不需要别的进程fork()出来,因此他是无父无母的石头爆出来的,系统中的所有其他进程都是他的后代)将会收养(adopt)这些孤儿进程。换句话说:Linux系统保证任何一个进程(除了init)都有父进程,也许是其真正的生父,也许是其祖先init。

而前一种情况是:父进程有别的事情要干,不能随时执行wait()/waitpid()来确保回收僵尸资源。在这样的情形下,我们可以考虑使用信号异步通知机制,让一个孩子在变成僵尸的时候,给其父进程发一个信号,父进程接收到这个信号之后,在对其进行处理,在此之前想干嘛就干嘛,异步操作,大家happy。但是即便是这样也仍然存在问题:如果两个以上的孩子同时退出变僵尸,那么他们就会同时给其父进程发送相同的信号,而相同的信号将会被淹没。怎么解决这个问题,请关注:

林老师的新书:《Linux环境编程图文指南》

书号:ISBN 978-7-121-28075-7

以上内容也是该书节选片段,欢迎查询订购。

本文分享自微信公众号 - 秘籍酷(mijiku040)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-03-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • MQTT 大消息失败原因排查

    小组内使用 MQTT 协议搭建了一个聊天服务器,前天在测大消息(超过5000汉字)时,连接直接变得不可用,后续发送的消息全部都收不到回复。

    Dylan Liu
  • Android 硬件抽象层调用流程分析

    Android 从 5.0 开始使用新的相机 API Camera2 来代替之前的旧版本,从而支持更多的特性。

    glumes
  • Kotlin 中的 run、let、with、apply、also、takeIf、takeUnless 语法糖使用和原理分析

    在 Kotlin 有一些可以简化代码的语法糖,比如 run、let、with、apply、also、takeIf、takeUnless 等。

    glumes
  • 转载【阿里员工排查问题的工具清单,总有一款适合你】

    这是一篇来源于阿里内部技术论坛的文章,原文在阿里内部获得一致好评。作者已经把这篇文章开放到云栖社区中供外网访问。文章内容做了部分删减,主要删减掉了其中只有阿里内...

    用户5640963
  • 【AI白身境】深度学习从弃用windows开始

    很多人会说,Windows不适合写代码?各种各样类似于visual studio的IDE那么牛逼,还不适合写代码?

    用户1508658
  • Centos查看端口占用情况和开启端口命令

    有时启动应用时会发现端口已经被占用,或者是感觉有些端口自己没有使用却发现是打开的。这时我们希望知道是哪个应用/进程在使用该端口。

    拓荒者
  • 005.iSCSI客户端配置示例-Windows

    IQN名称:iqn.2016-10.com.example.system1:iscsi01

    木二
  • windows系统上jenkins自动化部署小结

    易兒善
  • 我是如何用Redis做实时订阅推送的

    先说一下领劵中心的项目吧,这个项目就类似京东app的领劵中心,当然图是截取京东的,公司的就不截了。。。

    JAVA葵花宝典
  • 你可能不知道的Docker资源限制

      默认情况下,容器是没有资源限制的,它会尽可能地使用宿主机能够分配给它的资源。Docker提供了一种控制分配多少量的内存、CPU或阻塞I/O给一个容器的方式,...

    Edison Zhou

扫码关注云+社区

领取腾讯云代金券