|
导航:[首页]->[linux]->[System,Popen的陷阱]
为了更好的处理僵尸进程的问题,我们通常会考虑忽略SIGCHID信号,或者明确的Catch之并且在内部做wait处理。
system和popen函数也因为简单易用经常被用到,不过这两个函数有些细节必须要注意,否则可能导致一些严重但是却很难查的bug
system函数在内部会临时忽略(SIG_IGN)SIGINT和SIGQUIT信号,同时阻塞(SIG_BLOCK)SIGCHLD信号。具体见这里。
对于前者,若你的程序的退出以来SIGINT信号,而此时若它正在system执行一个耗时的程序,那么你的程序可能不会及时响应,甚至你的关闭指令丢失如果没有确认。
对于SIGCHLD信号,分两种情况处理。
##忽略SIGCHLD信号
我们知道,当忽略SIGCHID信号之后,所有的wait调用都会失败,errno=10(No child processes),所以system,popen等任何隐式或显式地调用wait都会失败,具体案例见这里
解决办法就是在调用system等之前将SIGCHID改成默认或者明确的Catch。
##Catch SIGCHLD信号
对于system函数,无影响
但是对于popen函数,同样可能失败(10,No child processes),这是因为当进程退出时,立即收到SIGCHLD,若接下来立即在信号处理回调里面wait,那么我们非信号回调的wait都会因为优先级的关系而等不到而失败。
解决办法参考system函数,临时阻塞信号,完了再解除阻塞
sigset_t saveblock;
sigset_t sa_mask
sigemptyset(&sa_mask);
sigaddset(&sa_mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sa_mask, &saveblock);
// do sth
sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0);
对于其他手动调用的wait函数,也必须在实现阻塞SIGCHLD信号。
经测试,最新的popen也阻塞了SIGCHLD信号,在pclose中接触阻塞
以下代码,若去掉pclose一行,那么Sigfunc不会出发。否则则会触发
#include <unistd.h>
#include <signal.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <cassert>
void SigFunc(int no)
{
printf("recv no '%d'",no);
}
int main()
{
signal(SIGCHLD, SigFunc);
FILE* fp_ = popen("true","r");
pclose(fp_);
return 0;
}
##Sample
#include <unistd.h>
#include <signal.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <cassert>
int Popen(const char* cmd_line)
{
FILE* fp_ = popen(cmd_line,"r");
assert(fp_);
char buf_[4096];
fread(buf_,sizeof(buf_),1,fp_);
return pclose(fp_);
}
int MyPopen(const char* cmd_line)
{
int ret = 0;
__sighandler_t old_handler;
old_handler = signal(SIGCHLD, SIG_DFL);
ret = Popen(cmd_line);
signal(SIGCHLD, old_handler);
return ret;
}
int MySystem(const char *cmd_line)
{
int ret = 0;
__sighandler_t old_handler;
old_handler = signal(SIGCHLD, SIG_DFL);
ret = system(cmd_line);
signal(SIGCHLD, old_handler);
return ret;
}
void SigFunc(int no)
{
}
int main()
{
signal(SIGCHLD, SIG_IGN);
if(system("true") != 0)
printf("system error %d|%s\n",errno,strerror(errno));
else
printf("system succuss\n");
if(MySystem("true") != 0)
printf("MySystem error %d|%s\n",errno,strerror(errno));
else
printf("MySystem succuss\n");
//--------------------------------------------------------
if(Popen("echo -n test") != 0)
printf("Popen error %d|%s\n",errno,strerror(errno));
else
printf("Popen succuss\n");
if(MyPopen("echo -n test") != 0)
printf("MyPopen error %d|%s\n",errno,strerror(errno));
else
printf("MyPopen succuss\n");
//--------------------------------------------------------
signal(SIGCHLD, SigFunc);
if(system("true") != 0)
printf("system error %d|%s\n",errno,strerror(errno));
else
printf("system succuss\n");
if(MySystem("true") != 0)
printf("MySystem error %d|%s\n",errno,strerror(errno));
else
printf("MySystem succuss\n");
//--------------------------------------------------------
if(Popen("echo -n test") != 0)
printf("Popen error %d|%s\n",errno,strerror(errno));
else
printf("Popen succuss\n");
if(MyPopen("echo -n test") != 0)
printf("MyPopen error %d|%s\n",errno,strerror(errno));
else
printf("MyPopen succuss\n");
return 0;
}
system error 10|No child processes
MySystem succuss
Popen error 10|No child processes
MyPopen succuss
system succuss
MySystem succuss
Popen succuss
MyPopen succuss
|
|