详解僵尸进程与孤儿进程

一、僵尸进程:
如果一个进程在其终止的时候,自己就回收所有分配给它的资源,系统就不会产生所谓的僵尸进程了。那么我们说一个进程终止之后,还保留哪些信息?为什么终止之后还需要保留这些信息呢?
一个进程终止的方法很多,进程终止后有些信息对于父进程和内核还是很有用的,例如进程的ID号、进程的退出状态、进程运行的CPU时间等。因此进程在终止时,回收所有内核分配给它的内存、关闭它打开的所有文件等等,但是还会保留以上极少的信息,以供父进程使用。父进程可以使用 wait/waitpid 等系统调用来为子进程收拾,做一些收尾工作。
1、概念
Linux 系统中僵尸进程和现实中僵尸(虽然我也没见过)类似,虽然已经死了,但是由于没人给它们收尸,还能四处走动。僵尸进程指的是那些虽然已经终止的进程,但仍然保留一些信息,等待其父进程为其收尸。
2、产生的过程
父进程调用fork创建子进程后,子进程运行直至其终止,它立即从内存中移除,但进程描述符仍然保留在内存中(进程描述符占有极少的内存空间)。子进程的状态变成EXIT_ZOMBIE,并且向父进程发送SIGCHLD 信号,父进程此时应该调用 wait() 系统调用来获取子进程的退出状态以及其它的信息。在 wait 调用之后,僵尸进程就完全从内存中移除。因此一个僵尸存在于其终止到父进程调用 wait 等函数这个时间的间隙,一般很快就消失,但如果编程不合理,父进程从不调用 wait 等系统调用来收集僵尸进程,那么这些进程会一直存在内存中。
3、生成一个僵尸进程
  1 #include<stdio.h>                                                                                                                                        
  2 #include <stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 int main()
  6 {
  7     pid_t id=fork();
  8     if(id<0)
  9     {
 10         perror("fork");
 11         return 0;
 12     }
 13     else if(id==0)
 14     {
 15 
 16         printf("I am a child,pid:%d,ppid:%d\n",getpid(),getppid());
 17         exit(0);
 18     }
 19     else
 20     {
 21          printf("I am a child,pid:%d,ppid:%d\n",getpid(),getppid());
 22         sleep(100);
 23     }
 24 return 0;
 25 }
[这里写图片描述](http://img.blog.csdn.net/20170505231115837?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2Vpemhlbmdibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
从上面可以看出,系统中多了一个僵尸进程。但如果等父进程睡眠醒来退出之后,我们再次查看系统进程信息,发现刚才的僵尸进程不见了。

这是为什么呢?父进程到死都也没有为其子进程收尸呀,怎么父进程退出之后,那个僵尸进程就消失了呢?难道父进程在退出时会为子进程收拾吗?其实不然....真正的原因是:父进程死掉之后,其所有子进程过继给 init 进程,init 进程成为该僵尸进程的新进程,init 进程会周期性地去调用 wait 系统调用来清除它的僵尸孩子。因此,你会发现上面例子中父进程死掉之后,僵尸进程也跟着消失,其实是 init 进程为其收尸的!
4、僵尸进程的危害
虽然僵尸进程已经释放了内存,但是还占用系统的内存资源(占用的内存是那些记录进程相关信息(如:进程ID(进程ID是有限的)、进程状态等信息)的内存,其他的全都释放了,但是如果系统中僵尸进程多了,就浪费了 大量的内存,如果很多,则会严重影响服务器的性能。
5、僵尸进程的避免
(1)父进程通过waitwaitpid等函数等待子进程结束,这会导致父进程挂起
(2)如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收
(3) 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收, 并不再给父进程发送信号
(4) 还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。
一、孤儿进程:
1、概念
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
注意:僵尸进程将会导致资源浪费,而孤儿则不会。
2、生成一个孤儿进程
 1 #include<stdio.h>                                                                                                                                        
  2 #include <stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>
  5 int main()
  6 {
  7     pid_t id=fork();
  8     if(id<0)
  9     {
 10         perror("fork");
 11         return 0;
 12     }
 13     else if(id==0)
 14     {
printf("I am a child,pid:%d,ppid:%d\n",getpid(),getppid());
 15         sleep(10);
 16         printf("I am a child,pid:%d,ppid:%d\n",getpid(),getppid());
 17     }
 18     else
 19     {
 20         sleep1);//保证原来的子进程先打印
 21          printf("I am a father,pid:%d,ppid:%d\n",getpid(),getppid());
 22          exit(0);//父进程在子进程前推出
 23     }
 24 return 0;
 25 }





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值