僵尸进程最优解
Posted 漂亮姐姐1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了僵尸进程最优解相关的知识,希望对你有一定的参考价值。
前言
什么是僵尸进程?
当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程,直到父进程结束,僵尸进程的资源才会被释放。
僵尸进程产生的原因
父子进程运行是一个异步的过程,为了使得父进程在子进程退出时,仍可以获得子进程的状态信息,Unix不会直接释放进程的所有资源。当子进程退出时,内核释放子进程的用户资源,包括打开的文件,占用的内存等,但是仍然为其保留一定的信息,包括进程号(Process ID),退出状态(Termination Status),运行时间(Runtime)等。
这样,父进程就可以通过调用wait/waitpid来获取子进程的状态信息同时释放子进程资源,但如果不调用wait/waitpid,系统资源将被占用,可能导致系统无法产生新的进程。
正文
正如上文提到的,可以使用wait/waitpid来将僵尸进程资源回收
可以在父进程中调用wait/waitpid,或者捕获SIGCHLD再调用wait/waitpid
需要注意:
- 调用wait/waitpid函数时,如果没有僵尸子进程,但仍有子进程,父进程会阻塞,直到有子进程死亡,变成僵尸进程
- wait/waitpid 一次返回一个子进程的状态信息,有多个进程需要调用多次
- 多线程阻塞在wait/waitpid时,仅有一个线程会返回子进程状态信息
大多时候我们都希望父子进程异步工作,而不是阻塞等待子进程结束,下面是捕获SIGCHLD的示例:
void handle(int sig)
printf("catch signal %d\\n",sig);
wait(NULL); // 可以传入结构体,并调用不同函数获取特定的信息,具体看手册
printf("Child process exit\\n");
int main()
int pid = fork();
if(pid > 0)
signal(SIGCHLD,handle);
// 父进程工作
else if(pid == 0)
// 子进程工作
但是这里忽略了SIGCHLD信号的产生条件,SIGCHLD信号在子进程终止,挂起,被唤醒时会向父进程发出
如果子进程接收到挂起信号,父进程进入handle处理函数,父进程将阻塞在wait/waitpid
如果我们不关心子进程的执行情况,最简单直接的方法就是忽略SIGCHLD信号,让子进程结束后由init接管,init进程会释放掉所有僵尸进程的资源。
signal(SIGCHLD,SIG_IGN);
思考一下,忽略SIGCHLD信号是否对程序有影响,SIGCHLD信号产生条件:终止,挂起,唤醒
其中挂起信号(SIGSTOP),SIGKILL信号无法捕获,所以忽略SIGCHLD信号对这两个没影响
但是假如我们希望捕获SIGCONT信号,生成日志,那么我们就不能简单忽略SIGCHLD信号
个人认为更科学的做法是
使用sigaction,并设置sa_flags标志位为SA_NOCLDWAIT
这样,当子进程终止(terminate)时,子进程不会被设置为僵尸进程
struct sigaction sa;
memset(&sa,0,sizeof(sa));
sa.sa_handler = SIG_DFL; // 也可以自定义handler函数
sa.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD,&sa,NULL);
tips:
当使用 -std=c99 编译时,会找不到sigaction结构体,解决方法:添加头文件<bits/sigaction.h>
也可以使用 -std=gnu99 编译
另外也有别的技巧,调用两次fork函数,然后子进程退出,孙进程由系统接管
int pid;
if ((pid = fork()) == 0)
if ((pid = fork()) > 0)
exit(0); // 子进程退出
else
// 孙进程工作
else
// 父进程工作
仅做记录,最好的方法还是使用sigaction
以上是关于僵尸进程最优解的主要内容,如果未能解决你的问题,请参考以下文章