首先,我们来捋一捋这个函数的功能,如下:
我们应当知道的是,在用fork创建子进程后,父子进程的执行的先后顺序是不定的,这时,我们可以用wait函数,wait()会暂停当前进程的执行,直到有信号到来或者子进程结束。总的来说,wait()的作用就是阻塞父进程,等待子进程。
看完了上面的内容,想必你也清楚了为什么我们要使用wait函数,并且是在哪使用
我们是在父进程中使用wait(),可以不让父进程先于其产生的子进程结束,因为如果父进程结束了,而子进程还没有结束,那这个进程就会变成一个“孤儿进程”,他会被init进程收养(进程号为1),并由init进程对它们完成状态收集工作。
当然如果子进程结束了,但是父进程没有调用wait或者waitpid(),那么子进程的资源就无法得到收集释放,这时候的子进程被称为“僵尸进程”,会占用系通资源,是非常不好的,危害很大。
总结:wait用于等待子进程结束,回收并释放子进程资源
函数原型:pid_t wait(int *status)
函数参数:
①status作为一个整形值,用来保存子进程退出时的状态;
②如果status为NULL,表示忽略子进程退出时的状态;
③如果status不为空,wait函数会将子进程退出的状态存入status中,另外,子进程退出时的状态可以通过linux中的特定的宏(macro)来进一步测定退出状态。
返回值:
成功,返回子进程的进程号;
失败,返回-1
接下来通过一个例子进一步说明
#include<stdio.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t pid = fork();//创建进程
if(pid < 0)
{
perror("fork error");
return -1;
}
else if(pid == 0)//子进程进入这里
{
int i = 0;
while(1)
{
if(10 == i)
{
break;
}
printf("这是子进程\n");
sleep(3);
i++;
}
exit(i);
}
else //父进程进入这里
{
int status = 0;
wait(&status);
if(WIFEXITED(status) != 0)//判断是否正常退出,如果是,就返回一个非零值
{
printf("子进程的返回值是%d\n",WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("子进程被信号%d\n",WTERMSIG(status));//此处检查子进程因何异常终止
}
while(1)
{
printf("这是父进程\n");
sleep(3);
}
}
return 0;
}
1、首先,生成可执行文件
2、通过命令ps -ef可以查看进程号
3、可以看出的是2号应当是子进程,1号应当是父进程,这一点通过他他们相同的783就可看出
结果: 结果说明:右侧是通过另一界面上发出的指令,通过kill命令杀死了子进程,并在父进程中返回了被杀死的情况。
以上展示的是非正常结束的结果,下面展示正常结束子进程的结果
说明:可以看出的是,在正常结束的情况下,程序在父进程中,WIFEXITED(status)的判断为正常终止,并通过WEXITSTATUS(status)返回了子进程的退出状态。
说完了wait(),我们再来说说waitpid(),可以说,waitpid是wait的升级版,具体我们看下面
函数原型:pid_t waitpid(pid_t pid ,int *status , int options)
可见,waitpid相比于wait多了两个参数,下面具体来说一下这两个参数的用法
参数pid:
输入的pid | 作用 |
pid > 0 | 相当于制定了进程号为pid的子进程,进行等待 |
pid = 0 | 等待同一个进程组中的任何子进程 |
pid = -1 | 等待任何一个子进程的退出,此时与wait作用一样 |
pid < -1 | 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值 |
参数options:
输入的options | 作用 |
WNOHANG | 若子进程并不立即可用,则waitpid不执行,此时返回值为0 |
WUNTRACED | 很少用到,我暂时也说不清楚,就不误导了 |
0 | 同wait,阻塞父进程,等待子进程退出 |