首先,wait是waitpid函数的简单版本,接下来我们对二者进行比较和实验。
一、waitpid
waitpid函数原型 pid_t waitpid ( pid_t pid , int *statusp , int options );
1、pid — 判定等待集合的成员
pid>0 | 指定一个单独的子进程,他的进程ID就是pid |
pid=0 | 同一进程组的任意一个进程 |
pid=-1 | 任意一个子进程 |
pid<-1 | 被指定的进程组中的任何子进程,这个进程组的ID就是pid的绝对值。 |
2、options — 修改默认行为
默认 option=0 | waitpid挂起调用进程的执行,直到他的等待集合中的一个子进程终止。如果等待集合中的一个进程在刚调用的时候就已经终止了,那么waitpid就立即返回 |
WNOHANG | 如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0) |
WUNTRACED | 挂起调用进程的执行,直到等待集合中一个正在运行的进程终止或等待集合中一个被停止的进程收到SIGCONT信号重新开始执行 |
WNOHANG|WUNTRACED | 立即返回。如果等待集合中的子进程都没有被停止或终止,则返回0;如果有一个停止或终止,则返回值为该子进程的PID |
3、检查已回收的子进程的退出状态
WIFEXITED(status) | 如果子进程通过调用exit或者一个返回(return)正常终止,就返回true |
WEXITSTATUS(status) | 返回一个正常终止的子进程的退出状态。只有在WIFEXITED()返回true时,才会定义这个状态 |
WIFSIGNALED(status) | 如果子进程时因为一个未被捕获的信号种植的,那么就返回true |
WTERMSIG(status) | 返回导致子进程终止的信号的编号。只有在WIFSIGNALED()返回true时,才定义 |
WIFSTOPPED(status) | 如果引起返回的子进程当前时停止的,那么就返回ture |
WSTOPSIG(status) | 返回引起子进程停止的信号的编号。只有在WIFSTOPPED()返回ture时,才定义 |
WIFCONTINUED(status) | 如果子进程收到SIGCONT信号重新启动,则返回true |
4、错误条件
如果调用进程没有子进程,那么waitpid返回-1,并且设置error为ECHILD.
如果waitpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR.
waitpid应用①
#define N 10
void fork11()
{
int pid[N];
int i;
int child_status;
/**
循环10次,产生10个停止的子进程,10正常进行的父进程
*/
for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0)
exit(100+i); /* Child */
/**
遍历pid[]数组
*/
for (i = 0; i <N; i++) {
int wpid = waitpid(pid[i], &child_status, 0);
if (WIFEXITED(child_status))
printf("Child %d terminated with exit status %d\n",
wpid, WEXITSTATUS(child_status));
else
printf("Child %d terminate abnormally\n", wpid);
}
}
在这个例子中waitpid参数中的pid>0,option为0。 运行结果如下, 遍历数组时,每个父进程回收了与其对应的子进程(即被指定进程号的子进程),从结果看出,回收顺序是有序的。
waitpid应用②
我们现在来简单修改一下应用①的代码,使pid=-1,不再遍历数组
#define N 10
void fork11()
{
int pid;
int i;
int child_status;
/**
循环10次,产生10个停止的子进程,10正常进行的父进程
*/
for (i = 0; i < N; i++)
if ((pid = fork()) == 0)
exit(100+i); /* Child */
while((pid=waitpid(-1,&child_status,0))>0)
{
if (WIFEXITED(child_status))
printf("Child %d terminated with exit status %d\n",
pid, WEXITSTATUS(child_status));
else
printf("Child %d terminate abnormally\n", pid);
}
}
这个例子中pid=-1,即任意一个子进程,运行结果如下
从这里就可以看出来了,没有循环遍历,没有了指定pid,此时回收子进程是没有顺序的。
waitpid应用③
#include<stdio.h>
int main()
{
int pid ,result;
pid=fork();
if(pc<0) /* 如果fork出错 */
printf("Error occured on forking./n");
else if(pc==0){ /* 如果是子进程 */
sleep(10); /* 睡眠10秒 */
exit(0);
}
/* 如果是父进程 */
do{
result=waitpid(pc, NULL, WNOHANG); /* 使用了WNOHANG参数,waitpid不会在这里等待 */
if(pr==0){ /* 如果没有收集到子进程 */
printf("No child exited/n");
sleep(1);
}
}while(result==0); /* 没有收集到子进程,就回去继续尝试 */
if(result==pid)
printf("successfully get child %d/n", pr);
else
printf("some error occured/n");
}
这个例子中pid=-1,option设为了WNOHANG。waitpid会立即返回,不会等待。在这个程序中,waitpid会一直循环,直到有停止的子进程出现。运行结果如下。
各位在运行的时候很明显能发现,语句输出是有1s间隔的,10s之后waitpid才终于回收了被停止的子进程。
同时,我们也可以把参数换成WNOHANG|WUNTRACED,可以看到和这个例子一样的效果,大家可以都试一下。
二、wait
wait相比waitpid要简单很多。wait一般来说有两种参数
①无参
int main()
{
int pid;
pid=fork();
if(pid==0) printf("Child is here");
if(pid>0){
printf("Parent is here);
wait();
printf("Child is done");
}
}
这个例子很简单,单纯的就是父进程等子进程执行结束后再进行。
②wait(&status)
我们稍微把waitpid应用①的代码改一下,把waitpid改成wait
#define N 10
void fork11()
{
int pid[N];
int i;
int child_status;
/**
循环10次,产生10个停止的子进程,10正常进行的父进程
*/
for (i = 0; i < N; i++)
if ((pid[i] = fork()) == 0)
exit(100+i); /* Child */
/**
遍历pid[]数组
*/
for (i = 0; i <N; i++) {
int wpid = wait(&child_status);
if (WIFEXITED(child_status))
printf("Child %d terminated with exit status %d\n",
wpid, WEXITSTATUS(child_status));
else
printf("Child %d terminate abnormally\n", wpid);
}
}
一旦换成wait(&child_status),它便不再指定某一个子进程,而是选择任意一个停止的子进程进行回收,我们来看一下结果,验证一下结论