异步回收fork出的子进程(僵尸进程)
2012-05-27 14:10
134 查看
什么是僵尸进程
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被僵尸进程销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)
僵尸进程是怎么样产生
在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸。
如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。
但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程.
异步回收僵尸进程:
fork()之后,非阻塞(异步)等待子进程(回收僵尸)。
fork()之后,子进程和父进程分叉执行,僵尸进程的产生是因为父进程没有给子进程“收尸”造成的,又可以根据危害程度分为下述两类:
总体来说:当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程。
(1)当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程,父进程结束后僵尸被init进程回收。
(2)如果子进程结束了,但是父进程始终没有结束,那么这个僵尸将一直存在,而且随着exec,僵尸越来越多。
如下面的代码,在父进程执行的5s内,子进程将为僵尸:
[cpp]
viewplaincopyprint?
#include<stdio.h> #include<stdlib.h> #include<signal.h> #include<unistd.h> intmain(){ //子进程的pid intc_pid; intpid; if((pid=fork())){ //父进程 c_pid=pid; printf("Thechildprocessis%d\n",c_pid); sleep(5); exit(0); }else{ //子进程 printf("I'machild.\n"); exit(0); } }
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
intmain(){
//子进程的pid
intc_pid;
intpid;
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
sleep(5);
exit(0);
}else{
//子进程
printf("I'machild.\n");
exit(0);
}
}
如上面的代码,在父进程的5s内,子进程一直是僵尸!
因此,需要对僵尸进程进行回收,传统的回收方法是,使用wait()函数,等待子进程,wait()是阻塞模式的,当子进程没有结束之前,wait一直等待,不往下面的语句执行。
[cpp]
viewplaincopyprint?
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
intmain(){
//子进程的pid
intc_pid;
intpid;
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
//阻塞等待子进程
intstatus;
if((pid=wait(&status))!=-1&&pid==c_pid){
//成功回收子进程
printf("Thechildexitwith%d\n",WEXITSTATUS(status));
fflush(stdin);
}else{
printf("wait()fail.\n");
}
printf("Now,Thechildhasbeenexit,andIwillsleep.\n");
sleep(20);
exit(0);
}else{
//子进程
printf("I'machild.\n");
sleep(5);
exit(0);
}
}
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
intmain(){
//子进程的pid
intc_pid;
intpid;
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
//阻塞等待子进程
intstatus;
if((pid=wait(&status))!=-1&&pid==c_pid){
//成功回收子进程
printf("Thechildexitwith%d\n",WEXITSTATUS(status));
fflush(stdin);
}else{
printf("wait()fail.\n");
}
printf("Now,Thechildhasbeenexit,andIwillsleep.\n");
sleep(20);
exit(0);
}else{
//子进程
printf("I'machild.\n");
sleep(5);
exit(0);
}
}
如上面的代码,在子进程执行5秒后,即被回收,在夫进程的20秒内,子进程已经被结束,不再是僵尸。
但是这种利用wait()阻塞等待的方法也有一定的缺陷,那就是父进程必须等待子进程,无法做其他事情,如何非阻塞的等待子进程呢?
manwait,查看NOTES章节,可以找到:
子进程退出的时候,会发送SIGCHLD信号,默认的POSIX不响应,所以,我们只需要把处理SIGCHLD的函数自己实现就OK了,怎么作呢?
signal用于设置处理信号量的规则(或跳转到的函数)
[cpp]
viewplaincopyprint?
signal(SIGCHLD,handler);
voidhandler(intnum)
{
//我接受到了SIGCHLD的信号啦
intstatus;
intpid=waitpid(-1,&status,WNOHANG);
if(WIFEXITED(status))
{
printf("Thechildexitwithcode%d",WEXITSTATUS(status));
}
}
signal(SIGCHLD,handler);
voidhandler(intnum)
{
//我接受到了SIGCHLD的信号啦
intstatus;
intpid=waitpid(-1,&status,WNOHANG);
if(WIFEXITED(status))
{
printf("Thechildexitwithcode%d",WEXITSTATUS(status));
}
}
OK,全部代码如下,注意父进程不要再用wait阻塞啦!
[cpp]
viewplaincopyprint?
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
voidhandler(intnum){
//我接受到了SIGCHLD的信号啦
intstatus;
intpid=waitpid(-1,&status,WNOHANG);
if(WIFEXITED(status)){
printf("Thechild%dexitwithcode%d\n",pid,WEXITSTATUS(status));
}
}
intmain(){
//子进程的pid
intc_pid;
intpid;
signal(SIGCHLD,handler);
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
//父进程不用等待,做自己的事情吧~
for(inti=0;i<10;i++){
printf("Doparentthings.\n");
sleep(1);
}
exit(0);
}else{
//子进程
printf("I'machild.\n");
sleep(2);
exit(0);
}
}
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
voidhandler(intnum){
//我接受到了SIGCHLD的信号啦
intstatus;
intpid=waitpid(-1,&status,WNOHANG);
if(WIFEXITED(status)){
printf("Thechild%dexitwithcode%d\n",pid,WEXITSTATUS(status));
}
}
intmain(){
//子进程的pid
intc_pid;
intpid;
signal(SIGCHLD,handler);
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
//父进程不用等待,做自己的事情吧~
for(inti=0;i<10;i++){
printf("Doparentthings.\n");
sleep(1);
}
exit(0);
}else{
//子进程
printf("I'machild.\n");
sleep(2);
exit(0);
}
}
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被僵尸进程销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)
僵尸进程是怎么样产生
在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸。
如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。
但是如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程.
异步回收僵尸进程:
fork()之后,非阻塞(异步)等待子进程(回收僵尸)。
fork()之后,子进程和父进程分叉执行,僵尸进程的产生是因为父进程没有给子进程“收尸”造成的,又可以根据危害程度分为下述两类:
总体来说:当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程。
(1)当子进程结束之后,但父进程未结束之前,子进程将成为僵尸进程,父进程结束后僵尸被init进程回收。
(2)如果子进程结束了,但是父进程始终没有结束,那么这个僵尸将一直存在,而且随着exec,僵尸越来越多。
如下面的代码,在父进程执行的5s内,子进程将为僵尸:
[cpp]
viewplain
#include<stdio.h> #include<stdlib.h> #include<signal.h> #include<unistd.h> intmain(){ //子进程的pid intc_pid; intpid; if((pid=fork())){ //父进程 c_pid=pid; printf("Thechildprocessis%d\n",c_pid); sleep(5); exit(0); }else{ //子进程 printf("I'machild.\n"); exit(0); } }
如上面的代码,在父进程的5s内,子进程一直是僵尸!
因此,需要对僵尸进程进行回收,传统的回收方法是,使用wait()函数,等待子进程,wait()是阻塞模式的,当子进程没有结束之前,wait一直等待,不往下面的语句执行。
[cpp]
viewplain
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
intmain(){
//子进程的pid
intc_pid;
intpid;
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
//阻塞等待子进程
intstatus;
if((pid=wait(&status))!=-1&&pid==c_pid){
//成功回收子进程
printf("Thechildexitwith%d\n",WEXITSTATUS(status));
fflush(stdin);
}else{
printf("wait()fail.\n");
}
printf("Now,Thechildhasbeenexit,andIwillsleep.\n");
sleep(20);
exit(0);
}else{
//子进程
printf("I'machild.\n");
sleep(5);
exit(0);
}
}
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
intmain(){
//子进程的pid
intc_pid;
intpid;
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
//阻塞等待子进程
intstatus;
if((pid=wait(&status))!=-1&&pid==c_pid){
//成功回收子进程
printf("Thechildexitwith%d\n",WEXITSTATUS(status));
fflush(stdin);
}else{
printf("wait()fail.\n");
}
printf("Now,Thechildhasbeenexit,andIwillsleep.\n");
sleep(20);
exit(0);
}else{
//子进程
printf("I'machild.\n");
sleep(5);
exit(0);
}
}
如上面的代码,在子进程执行5秒后,即被回收,在夫进程的20秒内,子进程已经被结束,不再是僵尸。
但是这种利用wait()阻塞等待的方法也有一定的缺陷,那就是父进程必须等待子进程,无法做其他事情,如何非阻塞的等待子进程呢?
manwait,查看NOTES章节,可以找到:
子进程退出的时候,会发送SIGCHLD信号,默认的POSIX不响应,所以,我们只需要把处理SIGCHLD的函数自己实现就OK了,怎么作呢?
signal用于设置处理信号量的规则(或跳转到的函数)
[cpp]
viewplain
signal(SIGCHLD,handler);
voidhandler(intnum)
{
//我接受到了SIGCHLD的信号啦
intstatus;
intpid=waitpid(-1,&status,WNOHANG);
if(WIFEXITED(status))
{
printf("Thechildexitwithcode%d",WEXITSTATUS(status));
}
}
voidhandler(intnum)
{
//我接受到了SIGCHLD的信号啦
intstatus;
intpid=waitpid(-1,&status,WNOHANG);
if(WIFEXITED(status))
{
printf("Thechildexitwithcode%d",WEXITSTATUS(status));
}
}
OK,全部代码如下,注意父进程不要再用wait阻塞啦!
[cpp]
viewplain
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
voidhandler(intnum){
//我接受到了SIGCHLD的信号啦
intstatus;
intpid=waitpid(-1,&status,WNOHANG);
if(WIFEXITED(status)){
printf("Thechild%dexitwithcode%d\n",pid,WEXITSTATUS(status));
}
}
intmain(){
//子进程的pid
intc_pid;
intpid;
signal(SIGCHLD,handler);
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
//父进程不用等待,做自己的事情吧~
for(inti=0;i<10;i++){
printf("Doparentthings.\n");
sleep(1);
}
exit(0);
}else{
//子进程
printf("I'machild.\n");
sleep(2);
exit(0);
}
}
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
voidhandler(intnum){
//我接受到了SIGCHLD的信号啦
intstatus;
intpid=waitpid(-1,&status,WNOHANG);
if(WIFEXITED(status)){
printf("Thechild%dexitwithcode%d\n",pid,WEXITSTATUS(status));
}
}
intmain(){
//子进程的pid
intc_pid;
intpid;
signal(SIGCHLD,handler);
if((pid=fork())){
//父进程
c_pid=pid;
printf("Thechildprocessis%d\n",c_pid);
//父进程不用等待,做自己的事情吧~
for(inti=0;i<10;i++){
printf("Doparentthings.\n");
sleep(1);
}
exit(0);
}else{
//子进程
printf("I'machild.\n");
sleep(2);
exit(0);
}
}
相关文章推荐
- 异步回收fork出的子进程(僵尸进程)
- 异步回收fork出的子进程(僵尸进程)
- APUE之fork两次与僵尸进程的问题
- 僵尸进程回收signal(SIGCHLD, SIG_IGN)和signal(SIGPIPE, SIG_IGN); .
- 通过两次fork避免产生僵尸进程
- 为何要fork()两次来避免产生僵尸进程?
- fork() & 僵尸进程
- Linux的进程,线程以及调度(fork与僵尸,内存泄漏,task结构体,停止状态与作业控制)
- Linux高编之进程--------fork函数的同步与异步(兄弟子进程和父子孙进程示列)
- linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
- linux—fork的那些事(僵尸进程)
- linux 中 signal 用法回收子进程结束产生的僵尸进程
- Linux杀死fork产生的子进程的僵尸进程defunct
- fork两次如何避免僵尸进程收藏
- .linux进程知识 程序存储、crontab、fork与vfork、exec、_exit()、wait()与waitpid()、孤儿和僵尸 文件读写 文件锁、select、poll
- linux进程知识 程序存储、crontab、fork与vfork、exec、_exit()、wait()与waitpid()、孤儿和僵尸
- 【apue学习心得】两次fork避免僵尸进程
- fork两次如何避免僵尸进程
- linux c 多进程fork基本用法及阻塞和非阻塞方式回收
- linux进程知识 程序存储、crontab、fork与vfork、exec、_exit()、wait()与waitpid()、孤儿和僵尸