什么是进程等待
简单来说, 进程等待是进程的一种状态, 是父进程等待子进程退出时的一个过程
为什么要进程等待
简单来讲, 因为要避免产生僵尸进程.
进程在退出的时候, 会关闭所有的文件描述符, 释放在用户空间中分配的内存, 但是 PCB 仍会暂时保留, 里面还存放着进程的退出状态以及统计信息等.
当一个进程创建一个子进程时, 父进程往往需要读取子进程的运行结果, 如果子进程先于父进程退出, 父进程则无法获取子进程的退出状态, 子进程的一些残留信息将一直存在(也就是变成所谓的僵尸态), 久而久之, 便会造成内存泄漏.
如何通过进程等待解决上述问题
通过两个系统调用接口来实现进程等待:
pid_t wait(int *status);
// status: 进程状态, 若不关注可以置为 NULL
// wait 返回值: 成功返回子进程 pid, 失败返回 -1
pid_t waitpid(pid_t pid, int *status, int options);
// pid:
// pid = -1: 等待任意一个子进程
// pid > 0: 等待子进程 ID 为 pid 的子进程
// status: 进程状态, 若不关注可以置为 NULL
//options: 选项
//阻塞式等待: options 为 0
//非阻塞式等待: options 为 WNOHANG
因为当一个进程正常或者异常终止时, 内核会向父进程发送一个 SIGCHLD 信号. 父进程可以选择忽略该信号, 或者提供一个该信号发生时调用处理的函数. 对于这种信号, 系统默认是忽略它的, 所以父进程如果想要处理掉子进程就要主动调用系统提供的处理接口 — wait 和 waitpid
阻塞和非阻塞
阻塞: 简单来讲就是一直等待. 阻塞式等待, 为了完成某个功能, 选择一直等待
非阻塞: 简单来讲就是立即返回, 非阻塞式等待, 若果某个功能未完成, 选择立即返回
通过 wait 来实现进程等待
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
// 进程创建失败
perror("fork error");
exit(-1);
} else if (pid == 0) {
// 子进程
printf("child is running, pid is %d\n", getpid());
sleep(5);
exit(0);
} else {
// 父进程
int status = 0;
pid_t ret = wait(&status);
printf("waiting...\n");
// WIFEXITED(status): 获取进程终止状态, 正常终止返回 true
if (WIFEXITED(status) && ret == pid) {
// WEXITSTATUS(status): 获取进程退出码, 返回子进程的退出码
printf("wait child 5s success, return code of child is %d\n", WEXITSTATUS(status));
} else {
printf("wait child failed, return\n");
}
}
return 0;
}
通过 waitpid 来实现进程等待
阻塞式等待:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork error");
exit(-1);
} else if (pid == 0) {
printf("child is running, pid is %d\n"