僵尸进程和孤儿进程

Posted 做1个快乐的程序员

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了僵尸进程和孤儿进程相关的知识,希望对你有一定的参考价值。

  进程状态分为多种,有运行状态、浅度睡眠和深度睡眠状态、暂停状态、死亡状态,这些在小编的上一个博客中有所提到,如果对此有不懂的读者可以自行翻阅。除此之外,还有僵尸进程、孤儿进程和守护进程,许多同学搞不懂僵尸进程和孤儿进程,这篇文章会详细为大家进行讲解。

僵尸进程和孤儿进程

一、僵尸进程

1、僵尸进程的引出

  我们用小故事引出僵尸进程,从前有一个地主,他是一个守财奴,可有一天却暴毙于家中,府中的丫鬟发现后报官,知府中的捕快来到现场第一件事就是封锁现场,不让任何闲杂人入内,然后知府大人、捕快们入内勘察线索,来判断地主是死于他杀还是意外死亡,该怎么判断呢?通过现场留下的证据,以及地主体内器官、皮肤表面各种状态来进行分析判断。换一种情况,如果地主死后,府里的仆人们立马将案发现场收拾干净,将死者尸体入棺埋葬,此时再报案,衙门还能得出地主死亡的原因吗?–不能!只有捕快勘察现场,将死者进行分析后,其他人才能清理现场。

  上面的故事中,我们封锁现场,并采集证据的时候,死者此时是不能被移走或者清理的,此时死者就是处于僵尸状态。只有当我们采集完证据后才能清理现场,此时才算退出状态。

那么为什么要存在僵尸进程呢?
  分析:进程被创建的目的就是完成某种工作或任务,当任务完成的时候,调用方应该知道创建的进程任务完成情况。
   举个最简单的例子,我们平时main函数中最后要写return 0;,这个0是做什么的呢?main函数是由mainCRTStartup函数调用的,mainCRTStartup是由加载器调用的,加载器又是由操作系统调用的,所以main函数间接性是OS调用的, 那么我们main函数是不是完成任务了,有没有异常退出,我们操作系统也是需要知道的,这里就需要给操作系统一个返回值,所以就有了return 0; 我们下面进行验证。我们学一个命令 - echo $?, $?用来检测最近一个进程执行完毕时的退出码信息(就是那个return值)。换言之,main函数的返回值就是供我们查看该进程的完成的状态。

图一:proc.c
图二:运行结果及退出码
图一:proc.c
图二:运行结果及退出码
   观察上方两个不同的代码,对应输出结果和退出码信息也在图中,我们发现,针对不同的情况,main函数返回给操作系统的退出码是不同的,其中0就代表进程正常结束,136则表明进程异常退出。

经过以上分析,我们得出为什么要有僵尸进程。
  答:进程退出的信息(退出码),是会被暂时保存起来的(task_struct),如果没有人读取,此时,该task_struct相关数据就不应该被释放掉。这就是僵尸状态Z。(当有别人来读取我们的状态信息时,该僵尸进程才会被释放)

2、模拟僵尸进程

  在下面的代码中,我们调用系统函数fork(),创建子进程之后,子进程会跑5秒钟,父进程一直在运行,子进程在5秒后退出,我们创建子进程就是让其帮我们去做某些事情的,但是子进程运行的怎么样,父进程是不知道的,父进程压根没管,所以就相当于子进程已经退出,父进程并不进行相关的状态询问,此时子进程虽然已经结束了,但是要一直维持一种状态—僵尸状态。

图一:ZState.c
图二:脚本查看进程状态
  我们发现, **当子进程结束的时候,其进程状态由S+变为Z+,这个状态就是僵尸状态。**

3、总结

3.1 僵尸进程概念

  僵尸状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程,僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。关于父进程如何读取子进程的退出状态,我们后面再将。

3.2 僵尸进程危害

  进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就必须一直处于Z状态!
  但是维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB就要一直维护!
  那一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费!!因为数据结构对象本身就要占用内存。

二、孤儿进程

  Linux系统中,进程之间的关系主要是符字关系,我们的僵尸进程就是父子进程之间,因为子进程退出,父进程不管他了,此时子进程就是僵尸进程。 我们的孤儿进程同样也是描述父子进程关系的。

1、孤儿进程概念

  如果父进程先于子进程退出,此时子进程还在运行,那么此时的子进程就被称为孤儿进程。
  学习僵尸进程后我们知道一个进程退出后会产生退出码等信息,那么具有父子关系的进程之间,如果父进程先退出,导致子进程变为孤儿进程后,那这个孤儿进程不会永无止境的运行,他也会有退出的那一刻,他退出后也会变为僵尸进程,那么孤儿进程的退出码信息该怎么办呢?
  答:当一个进程变为孤儿进程后,这个进程会立即被系统领养。这个进程变为僵尸进程后,是由操作系统来回收的。(代表操作系统的进程1号进程)

2、模拟孤儿进程

  我们修改之前模拟僵尸进程的代码,我们让父进程5s后退出,子进程变成死循环。
  下面就是程序和运行结果,我们在运行结果中发现子进程开始子进程的ppid是11013,而11013就是父进程的pid,当运行5s后,父进程退出,此时子进程的ppid变为1,这里就说明父进程退出后,子进程立马被1号进程收养,而1号进程就是操作系统。在进程状态图中我们也可以发现,

图一:程序代码
图二:运行结果

3、总结

  孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。
  孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
  僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
  僵尸进程因为资源不会完全释放,因此有可能会造成资源泄漏,但是孤儿进程不会。

以上是关于僵尸进程和孤儿进程的主要内容,如果未能解决你的问题,请参考以下文章

多任务编程 -- 孤儿进程和僵尸进程

僵尸进程和孤儿进程

孤儿进程和僵尸进程

孤儿进程和僵尸进程

僵尸进程和孤儿进程

僵尸进程和孤儿进程