linux 僵尸进程消除方法分析比较

来源:互联网 发布:怎么连接tor网络 编辑:程序博客网 时间:2024/06/05 06:13

最近调试程序时,ps发现进程列表中存在大量僵尸进程(僵尸进程大量出现基本上都是由于频繁杀掉进程然后重启进程造成的,所以这个是先决条件),在网络搜索了下消除僵尸进程的方法,总结下基本有一下3种方法:


1)  在父进程中使用signal(SIG_CHLD, bury)函数注册SIG_CHLD信号的信号处理函数为bury。这样当子进程退出时,内核就会给父进程发送SIG_CHLD信号,父进程在收到SIG_CHLD信号后产生中断,进入函数名为bury的信号处理函数,释放子进程资源。这样就避免了子进程变成僵尸进程。

2) 在父进程中使用signal(SIG_CHLD, SIG_IGN)函数注册SIG_CHLD信号的处理方式为SIG_IGN(父进程声明他不管自己的孩子,自己的孩子也就变成了孤儿进程了)。这样在子进程退出时,内核就不会给父进程发送SIG_CHLD信号,取而代之直接让init进程托管退出后的子进程,这样init进程就负责子进程资源的释放工作。

3) 使用两次fork来创建子进程。首先A进程通过fork函数创建出B进程,在成功创建出的B进程中再成功创建出C进程。此时B进程功成身退,直接退出,留下A进程和C进程。由于C进程的父进程已经退出了,所以C进程变为孤儿进程,此时init进程负责托管C进程,并且当C退出时,init进程负责释放C进程的资源。这样也就避免了僵尸进程。


通过实际使用和对比,发现1)的方法最直接,但是效率最低,同时也危险。由于1)中采用的是让父进程产生中断进入中断处理函数,所以,造成父进程在某段时间内只能处理子进程的资源释放工作,效率很低。而且在我自己的使用过程中还出现了在父进程进入中断处理函数bury释放子进程时,导致父进程阻塞在wait语句那里,从而使整个父进程再也无法响应其他的事情,除非子进程退出,父进程才能从wait语句那里返回。这里是我当时的bury函数:

void bury(int sig)

{

      int pid = -1;

      int exit_stat = 0;

      int error_nu = 0;

      while(1)

      {

              pid = wait(&exit_stat);        //在调试中,有一次主进程阻塞在wait这里,原因是父进程启动了3个子进程,而只有一个子进程退出,其他两个都好着。这样父进程在为那一个子进程收尸后,由于其他两个自己还没有退出,所以父进程就阻塞在了这里。

              if(0 > pid)&&(ECHLD == errno)

              {

                    printf("no child needs to wait, exit bury!\n")

              }

              else if(0 > pid)

              {

                    printf("there is an error when wait child!\n");

                    error_nu++;

                    if(3 <= error_nu)

                    {

                             printf("too much error! force exitting bury!\n");

                             return;

                     }

              }

              else

              {

                         printf("an child exit!\n");

              }

       }

}

所以我不推荐使用方法1)。


我推荐使用方法2)和方法3),由于init负责子进程的资源释放,这样父进程就有更多的精力来处理自己的事情了。

在使用过程中还是发现2)和3)虽然好但是使用还是得注意一些细节,不然就达不到想要的效果!!!!!!

首先说下3),这个方法固然好,虽然C进程不会变成僵尸,但是由于B进程退出了,而A进程在之前也没有注册任何signal()函数,所以导致B在退出后变成僵尸进程。

并且我也发现要使得2)这个方法起作用,必须两个进程的父子关系是单纯的建立在fork的基础上的,比如A进程通过fork调用创建了进程B作为自己的子进程。而如果A和B的父子关系中还包含了其他的系统调用,例如A先调用fork创建子进程X,然后X在调用execl替换了自己的进程映像产生了新的进程B,此时,虽然A和B还保持父子关系,但是已经不是仅仅建立在fork基础上的父子关系了,此时如果在父进程A中使用2)的方法signal(SIG_CHLD, SIG_IGN),内核并不会将进程B托管给init,所以释放进程B资源的任务就必须由进程A来signal(SIG_CHLD, bury)进行了,否则B死后就会变成僵尸进程。


推荐的做法是对于3),做这样的处理:

             首先在A中注册signal(SIG_CHLD, SIG_IGN)保证当子进程B退出后,子进程B的资源会被init释放而不变为僵尸。(因为A和B的父子关系单纯的建立在fork上)

             然后,在A成功fork出B后,让B也成功fork出C,然后B功成身退,直接退出。 (此时C变成了孤儿进程,托管于init,并且由init负责释放进程资源,此时C和init是父子关系)

             最后,进程C调用系统调用execl将自己的进程映像替换为其他的进程D, 此时,D依然和init进程保存父子关系,依然托管于init,由init负责释放D的资源。

          

           这样就保证了B和C和D都不会出现僵尸进程,同时A可以高效率的完成自己工作而不被SIG_CHLD信号打断。


以上只是个人体会,如有更好的方法或者建议还望多多指教!!






0 0
原创粉丝点击