wait系列
转自 http://blog.csdn.net/todd911/article/details/15028511
1.wait函数和waitpid函数
当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号。因为子进程终止是个异步事件,所以这种信号也是内核向父进程发的异步通知。父进程可以选择忽略信号,或者提供一个该信号的处理函数,对于这种信号默认动作是忽略它。调用wait或waitpid的进程可能会发生什么情况:
- 如果其所有子进程都在运行,则阻塞。
- 如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态后返回。
- 如果它没有任何子进程,则立即出错返回。
#include <sys/wait.h> pid_t wait(int *statloc); //如果成功返回进程ID,0,如果出错返回-1. pid_t waitpid(pid_t pid, int *statloc, int options); //如果成功返回进程ID,0(使用WNOHANG选项),如果出错返回-1.
这两个函数的区别是:
- 在一个子进程终止前,wait使其调用者阻塞,而waitpid有一个选项,可使调用者不阻塞。
- waitpid并不等待在其调用之后的第一个终止子程序,它有若干个选项,可以控制它所等待的进程。
statloc是一个整型指针,如果statloc不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内,如果不关心终止状态,则可将该参数指定为空指针。可以使用一下四个宏来查看进程的终止状态。
实践:
#include <stdio.h> #include <sys/wait.h> #include <stdlib.h> void pr_exit(int status){ printf("status = %d\n", status); if(WIFEXITED(status)){ printf("normal terminaton, exit status = %d\n", WEXITSTATUS(status)); }else if(WIFSIGNALED(status)){ printf("abnormal termination, signal number = %d%s\n",WTERMSIG(status), #ifdef WCOREDUMP WCOREDUMP(status)?"(core file generated)" : ""); #else ""); #endif }else if(WIFSTOPPED(status)){ printf("child stopped, signal number = %d\n", WSTOPSIG(status)); } } int main(void){ pid_t pid; int status; if((pid = fork()) < 0){ perror("fork"); return -1; }else if(pid == 0){ exit(7); } if(wait(&status) != pid){ perror("wait"); return -1; } pr_exit(status); if((pid = fork()) < 0){ perror("fork"); return -1; }else if(pid == 0){ abort(); } if(wait(&status) != pid){ perror("wait"); return -1; } pr_exit(status); if((pid = fork()) < 0){ perror("fork"); return -1; }else if(pid == 0){ status /= 0; } if(wait(&status) != pid){ perror("wait"); return -1; } pr_exit(status); return 0; }
运行结果:
[yan@yanPC apue]$ ./a.out
status = 1792
normal terminaton, exit status = 7
status = 134
abnormal termination, signal number = 6(core file generated)
status = 136
abnormal termination, signal number = 8(core file generated)
status = 1792
normal terminaton, exit status = 7
status = 134
abnormal termination, signal number = 6(core file generated)
status = 136
abnormal termination, signal number = 8(core file generated)
waitpid可以等待一个特定的进程结束。对于waitpid函数中的pid参数的作用解释如下:
pid == -1 等待任意子进程,与wait等效。
pid > 0 等待进程ID等于pid的子进程。
pid == 0 等待其组ID等于调用进程组ID的任意子进程。
pid < -1 等待其组ID等于pid绝对值的任一子进程。
options参数使我们能进一步控制waitpid的操作。参数可以是0(不是用options),或是下表常量或运算的结果:
实践:
#include <stdio.h> #include <sys/wait.h> #include <unistd.h> int main(void){ pid_t pid; int status; if((pid = fork()) < 0){ perror("fork"); return -1; }else if(pid == 0){ sleep(5); _exit(0); } while(waitpid(pid,&status,WNOHANG) == 0){ printf("no terminated process.\n"); sleep(1); } printf("child terminated.\n"); return 0; }
运行结果:
yan@yan-vm:~/apue$ ./a.out
no terminated process.
no terminated process.
no terminated process.
no terminated process.
no terminated process.
child terminated.
no terminated process.
no terminated process.
no terminated process.
no terminated process.
no terminated process.
child terminated.
使用了WNOHANG,因为没有结束的子进程,waitpid直接返回。
2.waitid函数
Single Unix Specification的XSI扩展包括了另一个取得进程终止状态的函数--waitid,该函数类似于waitpid,但提供了更多的灵活性。
#include <sys/wait.h> int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); //若成功则返回0,出错则返回-1.
id参数的作用与idtype的值相关,详细如下表:
options参数是下表各标志的按位或:
infop参数指向siginfo结构的指针,该结构包含了有关引起进程状态改变的生成信号的详细信息。
实践:
#include <stdio.h> #include <sys/wait.h> #include <unistd.h> int main(void){ pid_t pid, pid2; siginfo_t info; if((pid = fork()) < 0){ perror("fork"); return -1; }else if(pid == 0){ sleep(4); _exit(0); } while((pid2 = waitid(P_PID,pid,&info,WNOHANG)) == -1){ printf("no terminated process %d.\n",pid2); sleep(1); } return 0; }
运行结果:
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
no terminated process -1.
.......
按照说明,waitid一直返回-1,也就是一直出错,再看下进程状态。
yan 5147 0.0 0.0 2008 280 pts/2 S 14:10 0:00 ./a.out
yan 5148 0.0 0.0 0 0 pts/2 Z 14:10 0:00 [a.out] <defunct>
yan 5148 0.0 0.0 0 0 pts/2 Z 14:10 0:00 [a.out] <defunct>
子程序已经僵死了,但是父进程无法回收它,这边真的不明白是什么原因,望高人指点~~~~
3.wait3和wait4函数
大多数UNIX系统实现了另外的两个函数wait3和wait4.历史上,这2个函数是UNIX系统的BSD分支沿袭下来的。它们提供的功能比POSIX.1函数wait,waitpid和waitid所提供的功能要多一个,这与附加参数rusage有关,该参数要求内核返回由终止进程及其所有子进程使用的资源汇总。
#include <sys/types.h> #include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> pid_t wait3(int *statloc, int options, struct rusage *rusage); pid_t wait4(pid_t pid, int *statloc, int option, struct rusage *rusage); //两个函数返回值,若成功,则返回进程ID,若出错,则返回-1.
资源统计信息包括用户CPU时间总量,系统CPU时间总量,页面出错次数,接受到信号次数等,下表列出了各个wait函数所支持的参数。
4.一种避免僵死进程的方法
使用2次fork,即使不对子进程进行回收也不会出现僵死进程。
#include <stdio.h> #include <sys/wait.h> #include <stdlib.h> int main(void){ pid_t pid; if((pid = fork()) < 0){ perror("fork"); return -1; }else if(pid == 0){ if((pid = fork()) < 0){ perror("fork"); return -1; }else if(pid > 0){ exit(0); }else{ sleep(2); printf("second child,parent pid = %d\n",getppid()); exit(0); } } if(waitpid(pid,NULL,0) != pid){ //回收第一次fork的子进程 perror("waitpid"); return -1; } return 0; }
运行结果:
yan@yan-vm:~/apue$ ./a.out
yan@yan-vm:~/apue$ second child,parent pid = 1
yan@yan-vm:~/apue$ second child,parent pid = 1
parent id为1,即被init收养了。