关于僵死进程的一些理解
在每个进程退出如调用exit或return的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等,但是进程并没有立马就消失,而是仍然为其保留一定的信息(包括进程号,退出状态,运行时间, 直到父进程通过wait/waitpid来处理时才进行释放。此时该进程处于僵尸状态,该进程成为僵死进程(Zombie Process)。
它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号, 那么它就一直保持僵死状态,此时即使是root用户也无法清理,如果这时父进程结束了,僵死的子进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵死进程,它产生的所 有僵死进程也跟着消失(每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init来接管他,成为他的父进程)。
利用命令ps,可以看到有标记为Z的进程就是僵死进程
僵死进程的避免
1、父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起
2、调用signal或sigset将SIGCHLD的配置设置为忽略,则不会产生僵死子进程。
Linux中也可使用这个,在一个程序的开始调用这个函数signal(SIGCHLD,SIG_IGN);
3、fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要父进程来做。
4、改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
当然,我们知道wait的函数原型为 pid_t wait(int *status),因此我们可以利用status参数来保存被收集进程退出时的一些状态,通过调用此时未释放的struct_task结构体里的一些内容来返回判断。