Chinaunix首页 | 论坛 | 博客
  • 博客访问: 147358
  • 博文数量: 23
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 326
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-26 10:49
个人简介

记忆总是会慢慢褪去,所以让文字记住一切~

文章分类

全部博文(23)

文章存档

2017年(5)

2016年(3)

2015年(9)

2014年(6)

我的朋友

分类: C/C++

2014-03-26 17:42:58

最近参与的项目设计linux中进程的fork以及exec相关的系统函数去调用shell命令。由于项目限制,在程序中不可用waitpid等待子进程运行结束(使用WNOHANG来确保waitpid立即返回,即使子进程仍在运行)。
写完程序做个简单的运行测试,发现出现了若干的僵尸进程(defunct/zombie),如下所示,于是寻求解决之道~~~

[root@scanner197 ~]# ps aux|grep perl
      root     10305  0.0  0.0      0     0 pts/2    Z+   21:38   0:00 [perl]
      root     10327  0.0  0.0      0     0 pts/2    Z+   21:38   0:00 [perl]
      root     10330  0.5  0.0      0     0 pts/2    Z+   21:39   0:00 [perl]
      root     10334  0.0  0.0   4032   700 pts/1    S+   21:39   0:00 grep perl


何为僵尸进程?
介绍的资料很多,这里套用网上一位作者写的:僵尸进程是指的父进程已经退出,而该进程结束之后没有进程接受,就成为僵尸进程进程。

僵尸进程有危害吗?
僵尸进程仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,一般由其父进程来处理,若父进程不存在则交给init进程处理。除此之外,僵尸进程不占用任何内存空间。那么不占用内存就没有危害吗?显然不是,但系统僵尸进程不断增加,会导致进程表爆满,从而会使得新的进程创建失败,这是最严重的结果,所有但我们的程序产生僵尸进程,就得修复它。
如何解决僵尸进程?
可以发现,进程退出,其实是父进程来帮助处理;若父进程不在, 则有系统init进程处理。所有要么父进程处理,要么父进程消失。
1. 父进程处理
其实在子进程结束运行后,会产生SIGCHLD信号,父进程处理可以选择两类, 一类是忽略,一类是调用处理函数(waitpid算在内)
1) 
忽略
    
很简单,在产生子进程之前加上一句signal(SIGCHLD,  SIG_IGN); 示例如下:

点击(此处)折叠或打开

  1. void execCmdWithoutWait(std::string cmd)
  2. {
  3.     signal(SIGCHLD, SIG_IGN); //to avoid defunct/zombie
  4.     //signal(SIGCHLD, process); //to avoid defunct/zombie
  5.     pid_t pid;

  6.     if((pid = fork()) < 0)
  7.     {
  8.         cout<<"fork() fail!"<<endl;
  9.         return false;
  10.     }
  11.     int status;
  12.     if(pid == 0) //child process
  13.     {
  14.         //exectue
  15.         if((execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL)) < 0)
  16.         {
  17.             cout<<"Execute cmd:"<<cmd<<" failed"<<endl;
  18.             return false;
  19.         }
  20.     }
  21.     else //parent
  22.     {
  23.         pid_t return_pid = waitpid(pid, &status, WNOHANG);
  24.         if(return_pid < 0) //fail
  25.         {
  26.             cout<<"waitpid error."<<endl;
  27.         }
  28.         cout<<"return pid="<<return_pid<<endl;
  29.      }
  30. }
  31. int main()
  32. {
  33.     while(true)
  34.     {
  35.         execCmdWithoutWait("/opt/bin/qtool.pl /home/niel /home/niel");
  36.         sleep(10);
  37.     }
  38.     return 0;
  39. }
2) 处理
    设置SIGCHLD信号对于的处理函数。代码示例如下:

点击(此处)折叠或打开

  1. void process(int sig)    //processing function should as the prototype void(*handler)(int)
  2. {
  3.     cout<<"Process SIGCHLD"<<endl;
  4.     pid_t pid_t = waitpid(-1, NULL, 0);    //here we wait pid, for no waiting requirements, we can do other processing.
  5.     cout<<"pid_t="<<pid_t<<endl;

  6. }

  7. void execCmdWithoutWait(std::string cmd)
  8. {
  9.     //signal(SIGCHLD, SIG_IGN); //to avoid defunct/zombie
  10.     signal(SIGCHLD, process); //to avoid defunct/zombie
  11.     pid_t pid;

  12.     if((pid = fork()) < 0)
  13.     {
  14.         cout<<"fork() fail!"<<endl;
  15.         return false;
  16.     }
  17.     int status;
  18.     if(pid == 0) //child process
  19.     {
  20.         //exectue
  21.         if((execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL)) < 0)
  22.         {
  23.             cout<<"Execute cmd:"<<cmd<<" failed"<<endl;
  24.             return false;
  25.         }
  26.     }
  27.     else //parent
  28.     {
  29.         pid_t return_pid = waitpid(pid, &status, 0);
  30.         if(return_pid < 0) //fail
  31.         {
  32.             cout<<"waitpid error."<<endl;
  33.         }
  34.         cout<<"return pid="<<return_pid<<endl;
  35.      }
  36. }
  37. int main()
  38. {
  39.     while(true)
  40.     {
  41.         execCmdWithoutWait("/opt/bin/qtool.pl /home/niel /home/niel");
  42.         sleep(10);
  43.     }
  44.     return 0;
  45. }
在上面的代码示例中,同样有waitpid语句来接受子进程的返回(当声明SIGCHLD信号对应处理函数后/或忽略该信号,waitpid语句可以移除)。以避免子进程成为僵尸进程。

2. 父进程消失
父进程结束,则该父进程所产生的僵尸进程都会递给init进程。则也有两类处理:一类是强行kill父进程,这就等同于中止程序的运行;另一类是,我们所写的代码中让父进程消失。
对于第一类,就是关闭程序,这里不做任何介绍。对于第二类,常用的方法是两次fork()。示例代码如下:

点击(此处)折叠或打开

  1. void execCmd(std::string cmd)
  2. {
  3.     pid_t pid;

  4.     if((pid = fork()) < 0)
  5.     {
  6.         cout<<"fork() for child_1 failed"<<endl;
  7.     }

  8.     if(pid == 0) //child_1
  9.     {
  10.         if((pid = fork()) < 0)
  11.         {
  12.             cout<<"fork() failed for child_2"<<endl;
  13.         }
  14.         else
  15.         {
  16.             if(pid == 0) //child_2
  17.             {
  18.                  //exectue
  19.                 if((execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL)) < 0)
  20.                 {
  21.                     cout<<"Execute cmd:"<<cmd<<" failed"<<endl;
  22.                 }
  23.             }
  24.             else
  25.             {
  26.                 exit(0);
  27.             }
  28.         }
  29.     }
  30.     else
  31.     {
  32.         pid_t return_pid = waitpid(pid, NULL, 0);
  33.         if(return_pid < 0)
  34.         {
  35.             cout<<"Waitpid failed!"<<endl;
  36.         }
  37.         cout<<"return pid="<<return_pid<<endl;
  38.     }
  39. }

  40. int main()
  41. {

  42.     while(true)
  43.     {
  44.         //execCmdWithoutWait("/opt/bin/qtool.pl /home/niel /home/niel");
  45.         execCmd("/opt/bin/qtool.pl /home/niel /home/niel");
  46.         sleep(10);
  47.     }
  48.     return 0;
  49. }
OK, 现在选哪个方法就取决与你了。相关资料在网上应该很多,重要的是,自己手动去做一遍,加深一下理解。如果在你的项目中恰巧用到了这些知识,想必大有裨益~~~~~


阅读(3263) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~