进程终止
进程终止分为两步
- 进程自己执行退出操作exit,释放占用的部分系统资源,之后向父进程发送退出信号,报告自己已经退出;
- 父进程相应子进程的进程退出信号,执行回收操作wait,找到已经退出的子进程,回收其中的统计信息,释放其进程控制块和系统堆栈,从而将进程彻底销毁。
系统调用wait()
作用:回收已经退出的进程
详细功能:父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经退出的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回,返回值就是销毁的那个子进程的pid;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止;如果没有子进程,会返回-1。
僵尸进程
一个子进程的一些资源如进程控制块和系统堆栈在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。系统所能使用的进程号是有限的,如果产生大量僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。当系统中出现了僵尸进程时,我们是无法通过 kill 命令把它清除掉的。但是我们可以杀死它的父进程,此时僵尸进程就会变成孤儿进程,从而被 init 进程所收养,这样 init 进程就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程。僵尸进程通过 ps 命令显示出来的状态为 Z(zombie)。
孤儿进程
一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程(没有了父进程)。孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。由于孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。
系统调用wait()
pid_t wait(int *status)
父进程调用 wait() 会一直阻塞,直到收到一个子进程退出的 SIGCHLD 信号,之后 wait() 函数会销毁子进程并返回。
如果成功,返回被收集的子进程的进程 ID;如果调用进程没有子进程,调用就会失败,此时返回 -1。
参数 status 用来保存被收集的子进程退出时的一些状态,如果对这个子进程是如何死掉的毫不在意,只想把这个子进程消灭掉,可以设置这个参数为 NULL。
系统调用waitpid()
pid_t waitpid(pid_t pid, int *status, int options)
作用和 wait() 完全相同,但是多了两个可由用户控制的参数 pid 和 options。
pid 参数指定一个子进程的 ID,表示只关心这个子进程退出的 SIGCHLD 信号。如果 pid=-1 时,那么和 wait() 作用相同,都是关心所有子进程退出的 SIGCHLD 信号。
options 参数主要有 WNOHANG 和 WUNTRACED 两个选项,WNOHANG 可以使 waitpid() 调用变成非阻塞的,也就是说它会立即返回,父进程可以继续执行其它任务。