一、孤儿进程
在 Unix/Linux 系统中,子进程是通过父进程创建的,且两者的运行是相互独立的,父进程永远无法预测子进程到底什么时候结束。为了保证父进程可以得到子进程结束时的状态信息,当一个进程调用 exit 命令结束自己的生命时,它并没有真正的被销毁。内核只是释放了该进程的所有资源,包括打开的文件、占用的内存等,但是留下一个称为僵尸进程的数据结构,这个结构保留了一定的信息(包括进程号 the process ID,退出状态,运行时间),这些信息直到父进程通过 wait()/waitpid() 来取时才释放。
定义
一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并由 init 进程对它们完成状态收集工作。
危害
孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了 init 进程身上,init 进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为 init,而 init 进程会循环地 wait() 它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init 进程就会出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。
二、僵尸进程
定义
一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵死进程。
危害
僵尸进程虽然不占有任何内存空间,但如果父进程不调用 wait() / waitpid() 的话,那么保留的信息就不会释放,其进程号就会一直被占用,而系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。
解决方法
-
父进程通过 wait 和 waitpid 等函数等待子进程结束:这会导致父进程挂起,所以这并不是一个好办法,父进程如果不能和子进程并发执行的话,那我们创建子进程就没有意义。并且一个 wait 只能解决一个子进程,如果有多个子进程就要用到多个 wait。
-
通过信号机制:子进程退出时,向父进程发送 SIGCHILD 信号,父进程处理 SIGCHILD 信号,在信号处理函数中调用 wait 处理僵尸进程。
-
fork两次:原理是使进程成为孤儿进程,从而其父进程变为 init 进程,通过 init 进程处理僵尸进程。具体操作为:父进程一次 fork() 后产生一个子进程随后立即执行 wait(NULL) 来等待子进程结束,然后子进程再次 fork() 后产生孙子进程,随后立即exit(0)。这样子进程顺利终止(父进程仅仅给子进程收尸,并不需要子进程的返回值),然后父进程继续执行。这时的孙子进程由于失去了它的父进程(即是父进程的子进程),将被转交给Init进程托管。于是父进程与孙子进程无继承关系了,它们的父进程均为Init,Init进程在其子进程结束时会自动收尸,这样也就不会产生僵死进程了。
-
kill 父进程:严格地来说,僵尸进程并不是问题的根源,罪魁祸首是产生出大量僵尸进程的那个父进程。因此可以把产生大量僵尸进程的那个元凶枪毙掉(也就是通过 kill 发送 SIGTERM 或者 SIGKILL 信号)。父进程被结束之后,它产生的僵死进程就变成了孤儿进程,这些孤儿进程会被 init 进程接管,init 进程会 wait() 这些孤儿进程,释放它们占用的系统进程表中的资源。
三、进程回收
进程回收函数分别是wait函数和waitpid函数,调用一次只能回收一个子进程
- 阻塞并等待子进程退出
- 回收子进程残留资源
- 获取子进程结束状态
wait
pid_t wait(int *status);
返回值
0:回收子进程对应的pid
1:回收失败,已经没有子进程了
参数
status:
通过status获取子进程的退出状态
WIFEXITED(status),若为正常结束子进程返回的状态,则为真;对于这种情况可执行 WEXITSTATUS(status),取子进程传给exit的低8位。
WEXITSTATUS(status),取得子进程exit()返回的结束代码,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。
WIFSIGNALED(status),若为异常结束子进程返回的状态,则为真;对于这种情况可执行WTERMSIG(status),取使子进程结束的信号编号。
WTERMSIG(status),取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED 来判断后才使用此宏。
WIFSTOPPED(status),若为当前暂停子进程返回的状态,则为真;对于这种情况可执行WSTOPSIG(status),取使子进程暂停的信号编号。
WSTOPSIG(status),取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。
waitpid
pid_t waitpid(pid_t pid, int *status, int options);
参数
pid:
pid<-1 等待进程组号为pid绝对值的任何子进程
pid=-1 等待任何子进程,此时的waitpid()函数就退化成了普通的wait()函数
pid=0 等待进程组号与目前进程相同的任何子进程
pid>0 等待进程号为pid的子进程(一般是这个比较常用)
options:
WNOHANG,若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若结束,则返回该子进程的ID。
WUNTRACED,若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会。通过WIFSTOPPED(status)宏确定返回值是否对应一个暂停的子进程。
以上内容来源于网络知识总结,如有侵权请私信联系立即删除:)