前言:wait() 和 waitpid() 函数在C语言中有释放子进程遗留资源的作用。当一个子进程结束时调用exit()或_exit(),会进入僵死状态,它的资源(如内存、打开的文件描述符等)并不会立即被系统回收。这些资源会保留,直到父进程调用 wait() 或 waitpid() 来收集子进程的状态信息。在父进程中调用wait()和waitpid(),会等待子进程结束,期间父进程会进入阻塞状态,直到接收到子进程结束的信号,才会继续执行。如果有多个子进程,那使用一个wait(NULL),父进程只会等待其中的一个。可以多次调用来等待所有的子进程都就结束。
一、wait()
函数头文件 #include <sys/types.h>
#include <sys/wait.h>
函数原型 : pid_t wait(int *wstatus);
函数功能 : 让函数调用者进程进入到睡眠状态,等待子进程进入僵死状态后,释放相关资源并返回
函数参数: wstatus:保存子进程退出状态值变量的指针(*****获取具体值需要使用宏定义*****)
函数返回值 wait(): 如果成功,返回终止子进程的进程ID;如果出错,则返回-1
实际上是有4种返回值,还有返回0和返回<-1,但这些都代表特定的情况,并不常见。
对于传入的参数wstatus,一些宏可以对参数进行操作:
- WEXITSTATUS(status): 如果子进程是正常退出的,这个宏可以用来获取子进程的退出状态码。
- WTERMSIG(status): 如果子进程是因为信号而终止的,这个宏可以用来获取导致子进程终止的信号。
- WSTOPSIG(status): 如果子进程是被停止的,这个宏可以用来获取导致子进程停止的信号。
- WIFEXITED(status): 检查子进程是否正常退出。如果返回非零值,表示子进程是正常退出的。
- WIFSIGNALED(status): 检查子进程是否因接收到信号而终止。如果返回非零值,表示子进程是因为信号而终止的。
- WIFSTOPPED(status): 检查子进程是否被停止(比如通过 SIGSTOP 信号)。如果返回非零值,表示子进程当前是停止状态
在linux多进程中,进程之间是可以发送信号的,我们可以使用这些宏来获取信号和退出的信息,得到一个布尔值,非零为真,零为假,也可以将值打印出来。
如果不关心它的信息,那参数就传一个NULL,wait(NULL)等待子进程结束。
二.waitpid()
waitpid是可以选择在等待子进程结束是可以选择阻塞或非阻塞,并且是可以指定等待进程号为pid的子进程;还需要说的是调用wait(&wstatus)相当于:Waitpid (-1, &wstatus, 0),两者的实现的功能相同。
二、使用步骤
1.wait()
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main()
{
pid_t pid1 = fork(); //创建一个pid1子进程;
if(pid1 == -1){
perror("fork:");
exit(EXIT_FAILURE);
}else if(pid1 == 0){ //pid1的代码部分;
printf("child one\n");
sleep(2);
printf("child one end\n");
exit(EXIT_SUCCESS);
}else{ //主进程代码部分;
printf("parent\n");
pid_t pid2 = fork(); //再创建一个pid2子进程;
if(pid2 == -1){
perror("pid2:\n");
exit(EXIT_FAILURE);
}else if(pid2 == 0){ //pid2的代码部分;
printf("child twe\n");
sleep(6);
printf("child twe end\n");
exit(EXIT_SUCCESS);
}else{
wait(NULL);//主进程代码部分;
wait(NULL);//调用了wait()函数两次,所以等待两个子进程都结束后主进程才会继续执行;
printf("parent end\n");
}
}
return 0;
}
可以看到主进程最先开始,然后再是子进程,即便子进程中已经sleep(),但主进程还是等待子进程结束后,才会结束。
注意:要熟悉这种创建的格式,有上面的主进程部分也可以一块写在下面。这样创建是规范的,因为fork()函数是有多个返回值的,在子进程中返回0,在主进程中返回它的pid,创建失败返回-1,所以使用了if else这样的格式,可以将主进程与子进程分开,不重叠。
如果不这样去写,那么fork()创建子进程后剩下的代码子进程的会执行一次,主进程也会执行一次。
2.waitpid()
第一个参数是给要等待的子进程的pid,-1则代表任意一个子进程,第二个参数这取决于你是否需要它子进程结束的信息,如果不需要就写NULL,第三个就是选择阻塞或非阻塞。
在阻塞模式下,与wait()的用法差不多,waitpid (-1, &wstatus, 0)或waitpid (-1, NULL, 0),
在非阻塞模式下,如果waitpid(-1,&wstatus,WNOHANG)这条语句执行时,子进程还未结束,那么主进程不会阻塞,可以继续执行下面的代码,当然如果你还需要等待子进程,可以将它加入到循环中,这样既可以等待子进程,又可以在等待的时间中去执行别的代码。