unix/linux中,子进程通过父进程创建。子进程的结束和父进程的运行是异步状态,在一个进程工作终止后他的父进程需要调用wait或waitpid系统调用取得子进程的终止状态。
孤儿进程
:一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将位init进程(pid=1)收养,并有init进程对他们进行状态的收集工作
孤儿进程的收集任务交由init进程处理,每当出现孤儿进程,内核将孤儿进程的父进程设置为init进程, init进程会循环wait他的子进程。因此孤儿进程没有什么危害。
僵尸进程:
一个进程使用fork创建子进程,如果子进程退出,儿父进程没有调用wait或waitpid获取子进程的状态信息,那么子进程的描述符仍然保存在系统中,这种进程为僵尸进程。
unix在每个进程退出的时候内核释放该进程所有资源,包括打开的文件占用的内存等,但是仍然保留一定信息如进程号,退出状态,运行时间等直到父进程通过wait/waitpid来获取时释放。若父进程不调用wait/waitpid,那么保留的信息不回释放,进程号会被一直占用,系统能使用的进程号是有限的,产生大量僵死进程将导致因没有可用的进程号导致系统不能产生新的进程。
解决系统产生大量僵死进程的方式是将产生大量僵死进程的父进程kill掉, 通过kill发送SIGTREM或者SIGKILL信号。kill掉父进程后,僵死进程就变成了孤儿进程,这些进程会被init进程接管,init进程会wait这些孤儿进程释放他们占用的系统进程表中的资源。
避免产生僵尸进程:一般为了防止产生僵尸进程,在fork子进程之后要wait子进程,同时子进程退出的时候内核会给父进程一个SIGCHLD信号,可以在父进程建立一个捕获SIGCHLD信号的信号处理函数,在函数体中调用wait/waitpid,就可以清理退出的子进程防止产生僵尸进程。
问题:在不会SIGCHLD信号调用wait清理退出进程仍然不能彻底避免产生僵尸进程。Unix信号一般是不排队的,有多个SIGCHLD信号在信号处理函数执行之前产生那么信号处理函数仅执行一次。正确的解决办法是:调用waitpid而不是wait,在信号处理函数中在一个循环内调用waitpid以获取所有已终止子进程的状态。指定WNOHANG选项,使waitpid在有尚未终止的子进程时非阻塞。(wait不行,会阻塞到现有的子进程中第一个终止为止)。