前言

在编写C程序的时候, 通过fork函数来创建新的进程, wait函数来等待子进程结束.

那么就有一个问题了, 什么情况下父进程需要等待子进程结束后继续执行呢? 如果需要等待子进程结束, 那直接将操作放到父进程执行不就行了么? 反正等着也是等着.

当然, 还有一种情况, 任务A 和任务 B 是后续操作的前提条件, 并且两个任务可以并行进行, 此时确实可以父进程执行一个, 子进程执行一个, 然后父进程等待子进程完成后继续操作. 但是, 我还是觉得这个解释并不是那么完美. 这完全可以通过进程通信实现啊.

因此, 我觉得wait函数的出现一定是有着其他用途的.

这个问题在我重翻操作系统之后有了答案.

为什么

众所周知, 进程在操作系统中运行的过程中是存在PCB(进程管理块)模块的. 它是进程的唯一标识符, 一般保存着PID运行状态资源信息等等.

进程在调用exit函数退出时, 会对占有的资源进行回收. 注意, 在exit函数回收资源的这段时间, 该进程仍然在运行中. 因此, 有一个资源是进程无法自己进行回收的, 就是PCB. 进程要运行就需要PCB, 而PCB回收的前提就是进程已经不在运行了. 也就是说, 进程自己是无法回收自己的.

那么PCB就无法回收了么? 当然不是, 该wait函数出场了. 既然进程自己无法回收自己, 那么就由父进程负责回收咯. 因此, 这么一个等待子进程结束的函数也就有了用武之地.

顺带一提, 在子进程exit执行结束, PCB资源回收之前的这段时间, 子进程的状态被称为僵尸态, 很形象了.

mac中的进程:

wait函数python_wait函数python

debian中的进程:

wait函数python_wait函数python_02

可以看到, PID为0的进程是所有进程的父进程. 在mac中, 所有进程都由/sbin/launchd进行创建, debian中则为kthreadd. 他们的作用就是对进程进行管理和调度. 父进程通过fork创建子进程, 之后子进程再通过exec更换为要执行的进程.

而这个PID为0的进程, 也是系统在运行过程中唯一一个不是通过fork创建的进程.

但是, 还有一个问题, 那就是如果父进程没有等子进程结束呢? 子进程结束的时候, 发现其父进程早就结束了, 那它的PCB 无法回收了么? 当然不会, 这个PID为0的祖宗进程, 或者称为root进程会定期扫描所有的僵尸进程, 并且代替其父进程对其进行回收.