一、 为什么要等待子进程退出?
等待子进程退出是为了确保父进程能够在子进程执行完毕后继续执行或者处理子进程的结果。在许多情况下,父进程需要等待子进程完成后才能继续执行,以确保正确的执行顺序和结果。
以下是一些等待子进程退出的主要原因:
-
资源回收:子进程退出后,它占用的系统资源(如内存)需要被释放。如果父进程不等待子进程退出,这些资源可能会一直保留,导致资源泄漏或者系统资源不足。
-
获取子进程的返回状态:父进程可能需要获取子进程的退出状态码(exit status),以了解子进程的执行结果。这个状态码通常用来表示子进程的执行情况,比如是否成功完成任务,以及是否发生了错误。
-
协调任务顺序:在一些情况下,父进程可能需要等待子进程完成某些任务后才能继续执行自己的任务。通过等待子进程退出,父进程可以确保任务的顺序正确执行。
-
处理子进程的输出:如果子进程产生了输出(比如标准输出或标准错误输出),父进程可能需要等待子进程退出后才能获取并处理这些输出。
父进程等待子进程退出 并收集子进程的退出状态,子进程退出状态不被收集,变成僵死进程(僵尸进程)。
二、什么是僵尸进程?
僵尸进程是指已经结束执行但其父进程尚未对其进行善后处理(回收其占用的系统资源)的子进程。在 Unix 和类 Unix 系统中,当一个进程结束执行时,它会留下一个称为僵尸进程的状态,此时系统仍会记录该进程的进程号和退出状态,但并未释放其占用的全部资源。
造成僵尸进程的主要原因是父进程没有调用相应的系统调用(如wait()
或waitpid()
)来获取子进程的退出状态信息。通常情况下,父进程需要调用这些系统调用来回收子进程的资源,并清理僵尸进程。
虽然僵尸进程本身并不会占用太多系统资源,但过多的僵尸进程可能会导致系统资源的浪费和进程表的混乱,甚至影响系统的稳定性。
下面展示有等待并收集子进程退出的状态:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main()
{pid_t pid;int cnt = 0;int status = 10;pid = vfork();if(pid > 0){wait(&status); // 等待并收集子进程退出状态printf("child quit, child status = %d\n", WEXITSTATUS(status));while(1){printf("cnt = %d\n", cnt);printf("this is father print, pid = %d,\
child pid = %d\n", getpid(), pid);sleep(2);}}else if(pid == 0){while(1){printf("this is child print, pid = %d\n", getpid());sleep(2);cnt++;if(cnt == 3){exit(3);}}}return 0;
}
可以看到,没有Z+的僵尸进程。
下面展示没有收集子进程退出状态的情况:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main()
{pid_t pid;int cnt = 0;int status = 10;pid = vfork();if(pid > 0){// wait(&status);printf("child quit, child status = %d\n", WEXITSTATUS(status));while(1){printf("cnt = %d\n", cnt);printf("this is father print, pid = %d,\
child pid = %d\n", getpid(), pid);sleep(2);}}else if(pid == 0){while(1){printf("this is child print, pid = %d\n", getpid());sleep(2);cnt++;if(cnt == 3){exit(3);}}}return 0;
}
可以发现,出现了Z+的僵尸进程。
总结:
在父进程调用 wait()
或 waitpid()
等函数等待子进程退出时,操作系统会将子进程的退出状态信息传递给父进程,并在父进程成功获取退出状态后,释放子进程占用的系统资源,包括内存、文件描述符等。这样,子进程就不再是僵尸进程,而是完全被回收的进程,不再占用系统资源。
wait 和 waitpid 的区别:
wait 使调用者阻塞, waitpid有一个选项,可以使调用者不阻塞。