子进程回收
文章目录
0. 背景
进程在终止时会关闭所有文件描述符
、释放在用户区
分配的内存,但是在内核区
该进程的进程控制块(PCB)仍被保留用于保存一些信息。
-
如果程序正常退出,PCB中保存的则是退出状态(exit() 函数的参数)。
-
如果程序是异常退出,PCB中保存的则是导致程序终止的信号。
-
该进程的**
父进程
可以调用wait
与waitpid
函数获取PCB 中保存的信息**,并彻底清除该进程。
1. wait 函数
1.1 函数原型
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status)
1.2 函数功能
父进程调用 wait
函数可以获得子进程终止信息,并且彻底清除子进程。具体功能如下:
- 阻塞父进程,等待子进程退出
- 获取子进程退出状态信息
- 回收子进程资源
1.3 函数参数
- @param:
status
是一个传出参数,应定义变量后传入变量地址,函数可以通过该参数传出子进程PCB中的信息。查看具体信息方法见1.5
小节。
1.4 函数返回值
- 成功:返回被终止子进程的进程ID
- 失败:返回 -1
1.5 使用宏获取传出参数所携带的状态信息
如果 wait
函数的实参非 NULL,而是 int 的实参指针,wait
函数会将子进程状态信息通过实参传出,通过系统提供的宏可以得知参数所携带的信息。
WIFEXITED(status)
: 子进程如果是通过如下:exit()
函数、_exit()
、main 函数 return
方式正常结束,该宏返回true
。接下来可以通过WEXITSTATUS(status)
宏获得进程退出状态(即 exit() 函数的参数)。WIFSIGNALED(status)
: 如果该宏返回true
则子进程为异常退出,程序异常退出主要因为信号。接下来可以通过WTERMSIG(status)
获取导致进程终止的信号的编号。WIFSTOPPED(status)
: 进程处于暂停状态,则该宏返回true
。接下来,WSTOPSIG(status)
可以用于获取使进程暂停的那个信号的编号。若WIFCONTINUED(status)
为true
则进程暂停后已经开始继续运行。
1.6 代码示例
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h> // exit
int main(void)
{
pid_t pid = fork();
int status = 0;
if (pid > 0) {
pid_t 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)); // WEXITSTATUS 可以获取进程退出状态 (exit 的参数)
} else if (WIFSIGNALED(status)) {
// 异常退出,即被信号终止为 真
printf("The child process exit abnormally, "
"killed by signal %d\n", WTERMSIG(status));//获取信号编号
// 另一个终端 用 kill -9 pid 即可发送杀死信号
}
} else if (pid == 0) {
printf("I am child, my id is:%d\tmy parent id is:%d\n.",getpid(), getppid());
sleep(2);
#if 0
execl("05_abnormal", "05_abnormal", NULL); // 会触发 8 号信号 因为执行的程序会出现 除零错误
perror("execl error...");
exit(-1);
#endif
exit(10);
}
}
2. waitpid 函数
2.1 函数原型
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options)
2.2 函数功能
同 wait
函数相同,waitpid
函数也是用于回收子进程资源获取子进程结束状态信息。
2.3 函数参数
- @param:
pid
:> 0
: 回收指定 ID 的子进程-1
:回收任意子进程,waitpid(-1, status, 0)
等价于wait(status)
0
: 回收和当前调用waitpid
函数一个组的所有子进程< -1
: 回收指定进程组内的任意子进程
- @param:
status
: 是一个传出参数,应定义变量后传入变量地址,函数可以通过该参数传出子进程PCB中的信息。 - @param:
options
:0
:设置为阻塞状态,阻塞等待子进程结束,对其进行回收。WNOHSNG
:设置为不阻塞状态,此时如果没有子进程结束,则直接返回,该参数需要配合轮询方法使用。
2.4 函数返回值
- 成功:返回被终止子进程的进程ID
- 失败:返回 -1
2.5 使用宏获取传出参数所携带的状态信息
同上文 1.5 节
2.6 代码示例
#include <sys/stat.h> // waitpid
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
pid_t cpid, wpid;
int status;
int n = 5;
int i;
for (i = 0; i < n; i++) {
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (cpid == 0) {
break;
}
}
if (n == i) {
// parent
sleep(n);
printf("I am parent, pid is: %d.\n", getpid());
do
{
wpid = waitpid(-1, NULL, WNOHANG);
if (wpid > 0) {
n--;
}
sleep(1);
} while (n > 0);
printf("wait finished.\n");
} else {
sleep(i);
printf("I'm %dth child, pid = %d\n", i+1, getpid());
}
}