对于多进程而言,父进程一般需要跟踪子进程的退出状态。因此当子进程结束运行时,内核不会立即释放该进程的进程表的表项。以满足父进程后续对子进程退出的信息查询(死后验尸),当然前提是父进程还在运行。在子进程结束之后,父进程读取其退出状态之前,我们称该子进程处于僵尸态(用户空间已经被释放,其不能被调度)。
先介绍两个系统调用函数:
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status);// wait是一个阻塞函数,等待回收子进程,如果没有子进程则返回-1.成功返回子进程的id pid_t waitpid(pid_t pid, int *status, int options); // status 即是传入参数又是传出参数,通过传入地址来保存进程退出值,这里是int类型32位一部分是用来保存进程退出值,一部分是用来保存信号值。 //wait函数的阻塞显然不是服务器所期望的,而waitpid函数可以解决这个问题 <pre name="code" class="cpp">第一个参数:
< -1 回收指定进程组内的任意子进程
-1 回收任意子进程
0 回收和当前调用waitpid一个组的所有子进程
> 0 回收指定ID的子进程
options
WNOHANG没有子进程结束,立即返回(非阻塞)
WUNTRACED如果子进程由于被停止产生的SIGCHLD, waitpid则立即返回
WCONTINUED如果子进程由于被SIGCONT唤醒而产生的SIGCHLD, waitpid则立即返回
获取status
WIFEXITED(status)子进程正常exit终止,返回真
WEXITSTATUS(status)返回子进程正常退出值
WIFSIGNALED(status)子进程被信号终止,返回真
WTERMSIG(status)返回终止子进程的信号值
WIFSTOPPED(status)子进程被停止,返回真
WSTOPSIG(status)返回停止子进程的信号值
WIFCONTINUED(status)子进程由停止态转为就绪态,返回真
#include<stdio.h> #include <sys/types.h> #include <sys/wait.h> #include<unistd.h> int main() { pid_t pid=fork(); if(pid<0) { perror("fork"); return 0; } if(pid==0) { printf("child process pid:%d \n",getpid()); sleep(10); return 12; } else { int status=0; printf("parent process pid:%d \n",getpid()); printf("wait pid:%d \n",wait(&status)); //wait是阻塞的 printf("process exit value:%d \n",WEXITSTATUS(status));//print 进程的退出值 printf("signal value:%d \n",WTERMSIG(status));//print 信号值 kill -9 杀死子进程就会打出9 } return 0; }
目前有两种方法来进行回收僵尸进程1.wait阻塞方式2.waitpid的轮询模式。这两种方式都会导致父进程无法干别的事。
接下来我们将来讲述如何使用SIGCHLD处理僵尸进程
SIGCHLD的产生条件默认处理方式是忽略
1.子进程终止时候
2.子进程收到SIGSTOP信号停止时
3.子进程处于停止态,接受SIGCONT后唤醒时
#include<stdio.h> #include <errno.h> #include<signal.h> #include<sys/types.h> #include<sys/wait.h> #include<unistd.h> void sig_child(int num) { int status; pid_t pid; while((pid=waitpid(-1,&status,WNOHANG))>0)//回收子进程非阻塞方式 { if (WIFEXITED(status)) { printf("child %d exit %d\n", pid, WEXITSTATUS(status)); }else if(WIFSIGNALED(status)) { printf("child %d cancel signal %d\n", pid, WTERMSIG(status)); } } } int main() { pid_t pid; int i=0; for(i=0;i<4;i++) { if((pid=fork())==0) //如果是子进程则直接退出循环,由父进程fork出四个子进程 { break; } else if(pid<0) { perror("fork error "); return 0; } } if(pid==0) { printf("child process pid:%d \n",getpid()); sleep(20); return 10; } else { struct sigaction sigact; sigact.sa_handler=sig_child;//信号响应函数 sigact.sa_flags=0;//选择第一种函数类型(根据 struct sigaction 结构体来的) sigemptyset(&sigact.sa_mask);//置0,SIGCHLD 默认是忽略的 sigaction(SIGCHLD,&sigact,NULL); //注册信号 while(1) { printf("parent id is:%d \n",getpid()); sleep(1); } } return 0; }
另打开一个终端输入 kill -9 2229,可以看到进程id为2229是被信号杀死的。
版权声明:欢迎转载,如有不足之处,恳请斧正。