僵尸进程与孤儿进程
这部分参考了: https://www.cnblogs.com/Anker/p/3271773.html
linux提供一种机制使子进程在退出时候,父进程能够收集到子进程的结束状态信息(子进程pid,退出状态,运行时间等)。父进程需要调用 wait/waitpid来获取这些信息。父进程收集这些信息后这些信息才会释放。
linux下新进程的创建可以由fork来产生新的子进程。然后根据fork的返回值(小于0,等于0,大于0)判断是fork出错,子进程还是父进程。通常情况下,父进程需要在子进程任务结束退出后做“善后”,也就是一些资源清理工作。子进程退出时会把打开的文件句柄,内存占用,打开的资源进行释放,但是不会清理进程控制块PCB信息。
孤儿进程:
父进程早于子进程退出时候子进程还在运行,子进程会成为孤儿进程。linux会对孤儿进程的处理,把孤儿进程的父进程设为1,也就是由init进程来托管。init进程负责子进程退出后的善后清理工作。
僵尸进程:
子进程退出后留下的进程信息没有被收集,会导致占用的进程控制块PCB不被释放,形成僵尸进程。进程已经死去,但是进程资源没有被释放掉。
问题及危害
如果系统中存在大量的僵尸进程,他们的进程号就会一直被占用,但是系统所能使用的进程号是有限的,系统将因为没有可用的进程号而导致系统不能产生新的进程.。
孤儿进程的资源收集由init进程负责,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会处理它的一切善后工作。因此孤儿进程并不会有什么危害。
任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状程的身份对僵尸状态的子进程进行处理。
怎么避免僵尸进程
- 通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。
- 父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞。waitpid可以通过传递WNOHANG使父进程不阻塞立即返回。
- 如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。
- 通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。
- 杀死父进程。 如果僵尸进程的父进程还存在,找到这个父进程,kill掉它。这样就会变成2的情况,init会负责善后工作。