最近参与的项目设计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); 示例如下:
-
void execCmdWithoutWait(std::string cmd)
-
{
-
signal(SIGCHLD, SIG_IGN); //to avoid defunct/zombie
-
//signal(SIGCHLD, process); //to avoid defunct/zombie
-
pid_t pid;
-
-
if((pid = fork()) < 0)
-
{
-
cout<<"fork() fail!"<<endl;
-
return false;
-
}
-
int status;
-
if(pid == 0) //child process
-
{
-
//exectue
-
if((execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL)) < 0)
-
{
-
cout<<"Execute cmd:"<<cmd<<" failed"<<endl;
-
return false;
-
}
-
}
-
else //parent
-
{
-
pid_t return_pid = waitpid(pid, &status, WNOHANG);
-
if(return_pid < 0) //fail
-
{
-
cout<<"waitpid error."<<endl;
-
}
-
cout<<"return pid="<<return_pid<<endl;
-
}
-
}
-
int main()
-
{
-
while(true)
-
{
-
execCmdWithoutWait("/opt/bin/qtool.pl /home/niel /home/niel");
-
sleep(10);
-
}
-
return 0;
-
}
2) 处理
设置SIGCHLD信号对于的处理函数。代码示例如下:
-
void process(int sig) //processing function should as the prototype void(*handler)(int)
-
{
-
cout<<"Process SIGCHLD"<<endl;
-
pid_t pid_t = waitpid(-1, NULL, 0); //here we wait pid, for no waiting requirements, we can do other processing.
-
cout<<"pid_t="<<pid_t<<endl;
-
-
}
-
-
void execCmdWithoutWait(std::string cmd)
-
{
-
//signal(SIGCHLD, SIG_IGN); //to avoid defunct/zombie
-
signal(SIGCHLD, process); //to avoid defunct/zombie
-
pid_t pid;
-
-
if((pid = fork()) < 0)
-
{
-
cout<<"fork() fail!"<<endl;
-
return false;
-
}
-
int status;
-
if(pid == 0) //child process
-
{
-
//exectue
-
if((execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL)) < 0)
-
{
-
cout<<"Execute cmd:"<<cmd<<" failed"<<endl;
-
return false;
-
}
-
}
-
else //parent
-
{
-
pid_t return_pid = waitpid(pid, &status, 0);
-
if(return_pid < 0) //fail
-
{
-
cout<<"waitpid error."<<endl;
-
}
-
cout<<"return pid="<<return_pid<<endl;
-
}
-
}
-
int main()
-
{
-
while(true)
-
{
-
execCmdWithoutWait("/opt/bin/qtool.pl /home/niel /home/niel");
-
sleep(10);
-
}
-
return 0;
-
}
在上面的代码示例中,同样有waitpid语句来接受子进程的返回(当声明SIGCHLD信号对应处理函数后/或忽略该信号,waitpid语句可以移除)。以避免子进程成为僵尸进程。
2. 父进程消失
父进程结束,则该父进程所产生的僵尸进程都会递给init进程。则也有两类处理:一类是强行kill父进程,这就等同于中止程序的运行;另一类是,我们所写的代码中让父进程消失。
对于第一类,就是关闭程序,这里不做任何介绍。对于第二类,常用的方法是两次fork()。示例代码如下:
-
void execCmd(std::string cmd)
-
{
-
pid_t pid;
-
-
if((pid = fork()) < 0)
-
{
-
cout<<"fork() for child_1 failed"<<endl;
-
}
-
-
if(pid == 0) //child_1
-
{
-
if((pid = fork()) < 0)
-
{
-
cout<<"fork() failed for child_2"<<endl;
-
}
-
else
-
{
-
if(pid == 0) //child_2
-
{
-
//exectue
-
if((execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL)) < 0)
-
{
-
cout<<"Execute cmd:"<<cmd<<" failed"<<endl;
-
}
-
}
-
else
-
{
-
exit(0);
-
}
-
}
-
}
-
else
-
{
-
pid_t return_pid = waitpid(pid, NULL, 0);
-
if(return_pid < 0)
-
{
-
cout<<"Waitpid failed!"<<endl;
-
}
-
cout<<"return pid="<<return_pid<<endl;
-
}
-
}
-
-
int main()
-
{
-
-
while(true)
-
{
-
//execCmdWithoutWait("/opt/bin/qtool.pl /home/niel /home/niel");
-
execCmd("/opt/bin/qtool.pl /home/niel /home/niel");
-
sleep(10);
-
}
-
return 0;
-
}
OK, 现在选哪个方法就取决与你了。相关资料在网上应该很多,重要的是,自己手动去做一遍,加深一下理解。如果在你的项目中恰巧用到了这些知识,想必大有裨益~~~~~
阅读(2090) | 评论(0) | 转发(0) |