导航:[首页]->[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