wait函数
一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则保存着导致该进程终止的信号是哪个。这个进程的父进程可以调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。我们知道一个进程的退出状态可以在Shell中使用echo $?命令查看,因为Shell是它的父进程,当它终止时Shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。
父进程调用wait函数可以回收子进程终止信息。该函数有三个功能:
① 阻塞等待子进程退出
② 回收子进程残留资源
③ 获取子进程结束状态(退出原因)。
pid_t wait(int *status); //注意,int型的status变量需要外部创建,然后传入地址
成功:返回清理掉的子进程ID;失败:返回-1 (没有子进程)
当进程终止时,操作系统的隐式回收机制会:1.关闭所有文件描述符 2. 释放用户空间分配的内存。内核的PCB仍存在。其中保存该进程的退出状态。(正常终止→退出值;异常终止→终止信号)
可使用wait函数的传出参数status来保存进程的退出状态。借助宏函数来进一步判断进程终止的具体原因。宏函数可分为如下三组:
- WIFEXITED(status) 为非0 → 进程正常结束
WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数) - WIFSIGNALED(status) 为非0 → 进程异常终止
WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。 - WIFSTOPPED(status) 为非0 → 进程处于暂停状态
WSTOPSIG(status) 如上宏为真,使用此宏 → 取得使进程暂停的那个信号的编号。
WIFCONTINUED(status) 为真 → 进程暂停后已经继续运行
例1:
wait1.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid, wpid;
pid = fork();
if(pid == -1){
perror("fork error");
exit(1);
} else if(pid == 0)//子进程
{
printf("I'm process child, pid = %d\n", getpid());
sleep(7);
} else
{
wpid = wait(NULL);//父进程阻塞等待
if(wpid == -1)
{
perror("wait error");
}
printf("I'm parent, I catched child process,pid = %d\n", wpid);
}
return 0;
}

例2:
wait2.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid, wpid;
int status;
pid = fork();
if(pid == -1)
{
perror("fork error");
exit(1);
}
else if(pid == 0)
{ //子进程
printf("I'm process child, pid = %d\n", getpid());
#if 1
execl("./abnor", "abnor", NULL);//使用测试用例
perror("execl error");
exit(1);
#endif
sleep(1);
exit(10);
}
else
{
wpid = wait(&status); //传出参数
if(WIFEXITED(status)){ //正常退出
printf("I'm parent, The child process %d exit normally\n", wpid);
printf("return value:%d\n", WEXITSTATUS(status));
}
else if (WIFSIGNALED(status))//异常退出
{
printf("The child process exit abnormally,killed by signal %d\n", WTERMSIG(status));//获取信号编号
}
else
{
printf("other...\n");
}
}
return 0;
}
abnor.c
/*测试用例,用以验证wait异常情况*/
int main(void)
{
char *p = "test of wait abnormally\n";
//p[0] = 'h';
int a = 5/0;
return 56;
}
结果1:对应int a = 5/0;

结果2:对应p[0] = 'h';

结果3:对应屏蔽掉测试用例,此时子进程正常结束
#if 0
execl("./abnor", "abnor", NULL);
perror("execl error");
exit(1);
#endif

waitpid函数
作用同wait,但可指定pid清理子进程,而且可以不阻塞。
pid_t waitpid(pid_t pid, int *status, in options);
成功:返回清理掉的子进程ID;失败:-1(无子进程)
特殊参数和返回情况:
参数pid:
> 0, 回收指定ID的子进程
-1,回收任意子进程(相当于wait)
0,回收和当前调用waitpid一个组的所有子进程
< -1,如-i表示回收指定进程组i内的任意子进程
返回值为0的情况:参3为WNOHANG,且子进程正在运行时,此时返回值为0。
注意:一次wait或waitpid调用只能清理一个子进程,清理多个子进程应使用循环。
例1:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid,wpid;
pid = fork();
if(pid == -1)
{
perror("fork error");
exit(1);
}
else if(pid == 0)
{ //子进程
printf("I'm process child, pid = %d\n", getpid());
sleep(5);
exit(4);
}
else
{ //父进程
do {
wpid = waitpid(pid, NULL, WNOHANG);//不阻塞
if(wpid == 0)
{
printf("子进程%d还未被回收\n",pid);
sleep(1);
}
} while (wpid == 0);//轮询回收指定子进程
if(wpid == pid)
{ //成功回收了指定的子进程
printf("I'm parent, I catched child process,pid = %d\n", wpid);
}
else
{
printf("other...\n");
}
}
return 0;
}

例2:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(1);
}
if (pid == 0)
{
printf("This is the child\n");
sleep(5);
exit(34);
}
else
{
int stat_val;
waitpid(pid, &stat_val, 0);//指定pid回收子进程,阻塞
if (WIFEXITED(stat_val))
printf("Child exited with code %d\n", WEXITSTATUS(stat_val));
else if (WIFSIGNALED(stat_val))
printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val));
}
return 0;
}

例3:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int n = 5, i;
pid_t p;
if(argc == 2){
n = atoi(argv[1]);
}
for(i = 0; i < n; i++)//循环创建n个子进程
{
p = fork();
if(p == 0)
{
break;//子进程出口
}
}
if(n == i)
{ //父进程
sleep(n);
printf("I am parent, pid = %d\n", getpid());
do{
p = waitpid(0, NULL, WNOHANG);//回收任意子进程,非阻塞,因为有多个子进程,所以轮询回收
if(p>0)
{
printf("wait pid = %d\n", p);
n--;
}
sleep(1);
}while(n>0);
}
else
{
sleep(i);
printf("I'm %dth child, pid = %d\n", i+1, getpid());
}
return 0;
}

5029

被折叠的 条评论
为什么被折叠?



