异步回收fork出的子进程(僵尸进程)

  什么是僵尸进程

  一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被  僵尸进程销毁, 而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是 使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)

僵尸进程是怎么样产生

  在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸。

  如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。

  但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程.

 

异步回收僵尸进程:

fork()之后,非阻塞(异步)等待子进程(回收僵尸)。
fork()之后,子进程和父进程分叉执行,僵尸进程的产生是因为父进程没有给子进程“收尸”造成的,又可以根据危害程度分为下述两类:
总体来说:当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程。
(1)当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程,父进程结束后僵尸被init进程回收。
(2)如果子进程结束了,但是父进程始终没有结束,那么这个僵尸将一直存在,而且随着exec,僵尸越来越多。
如下面的代码,在父进程执行的5s内,子进程将为僵尸:

  1.     
  2. #include <stdio.h>   
  3. #include <stdlib.h>   
  4. #include <signal.h>   
  5. #include <unistd.h>   
  6.     
  7. int main() {   
  8.     //子进程的pid   
  9.     int c_pid;   
  10.     int pid;   
  11.     
  12.     if ((pid = fork())) {   
  13.         //父进程   
  14.         c_pid = pid;   
  15.         printf("The child process is %d\n", c_pid);   
  16.         sleep(5);   
  17.         exit(0);   
  18.     } else {   
  19.         //子进程   
  20.         printf("I 'm a child.\n");   
  21.         exit(0);   
  22.     }   
  23. }  

如上面的代码,在父进程的5s内,子进程一直是僵尸!
因此,需要对僵尸进程进行回收,传统的回收方法是,使用wait()函数,等待子进程,wait()是阻塞模式的,当子进程没有结束之前,wait一直等待,不往下面的语句执行。  

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <signal.h>   
  4. #include <unistd.h>   
  5. #include <sys/wait.h>   
  6.     
  7. int main() {   
  8.     //子进程的pid   
  9.     int c_pid;   
  10.     int pid;   
  11.     
  12.     if ((pid = fork())) {   
  13.         //父进程   
  14.         c_pid = pid;   
  15.         printf("The child process is %d\n", c_pid);   
  16.         //阻塞等待子进程   
  17.         int status;   
  18.         if ((pid = wait(&status)) != -1 && pid == c_pid) {   
  19.             //成功回收子进程   
  20.             printf("The child exit with %d\n", WEXITSTATUS(status));   
  21.             fflush(stdin);   
  22.         } else {   
  23.             printf("wait() fail.\n");   
  24.         }   
  25.         printf("Now , The child has been exit , and I will sleep.\n");   
  26.         sleep(20);   
  27.         exit(0);   
  28.     } else {   
  29.         //子进程   
  30.         printf("I 'm a child.\n");   
  31.         sleep(5);   
  32.         exit(0);   
  33.     }   
  34. }  

如上面的代码,在子进程执行5秒后,即被回收,在夫进程的20秒内,子进程已经被结束,不再是僵尸。
但是这种利用wait()阻塞等待的方法也有一定的缺陷,那就是父进程必须等待子进程,无法做其他事情,如何非阻塞的等待子进程呢?
man wait,查看NOTES章节,可以找到:
子进程退出的时候,会发送SIGCHLD信号,默认的POSIX不响应,所以,我们只需要把处理SIGCHLD的函数自己实现就OK了,怎么作呢?
signal用于设置处理信号量的规则(或跳转到的函数)

  1. signal(SIGCHLD,handler);   
  2. void handler(int num)   
  3. {   
  4.     //我接受到了SIGCHLD的信号啦   
  5.     int status;   
  6.     int pid = waitpid(-1,&status,WNOHANG);   
  7.     if(WIFEXITED(status))   
  8.     {   
  9.         printf("The child exit with code %d",WEXITSTATUS(status));   
  10.     }   
  11. }  

OK,全部代码如下,注意父进程不要再用wait阻塞啦!

  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <signal.h>   
  4. #include <unistd.h>   
  5. #include <sys/wait.h>   
  6.     
  7. void handler(int num) {   
  8.     //我接受到了SIGCHLD的信号啦   
  9.     int status;   
  10.     int pid = waitpid(-1, &status, WNOHANG);   
  11.     if (WIFEXITED(status)) {   
  12.         printf("The child %d exit with code %d\n", pid, WEXITSTATUS(status));   
  13.     }   
  14. }   
  15.     
  16. int main() {   
  17.     //子进程的pid   
  18.     int c_pid;   
  19.     int pid;   
  20.     
  21.     signal(SIGCHLD, handler);   
  22.     
  23.     if ((pid = fork())) {   
  24.         //父进程   
  25.         c_pid = pid;   
  26.         printf("The child process is %d\n", c_pid);   
  27.     
  28.         //父进程不用等待,做自己的事情吧~   
  29.         for (int i = 0; i < 10; i++) {   
  30.             printf("Do parent things.\n");   
  31.             sleep(1);   
  32.         }   
  33.     
  34.         exit(0);   
  35.     } else {   
  36.         //子进程   
  37.         printf("I 'm a child.\n");   
  38.         sleep(2);   
  39.         exit(0);   
  40.     }   
  41. }  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值