在popen函数调用之后,睡眠不在子进程中工作

问题描述 投票:0回答:1

我编写了一个小程序,它创建了一个子进程,然后使用popen运行shell命令。我创建了一个信号处理程序来等待子进程结束,而父进程只是在无限循环中运行。当我在popen系统调用后调用'sleep(5)'时,睡眠调用不起作用。

相反,如果我把睡眠呼叫放在popen之前,那么它正在工作。此外,如果我删除信号处理程序并等待父进程中的子进程,那么没有问题(即睡眠在任何地方正常运行)。

//Assume all required header files and function prototypes added

int main()
{
    int pid,status;
    signal(SIGCHLD,reaper);
    pid = fork();
    if(pid == 0)
       runCommand();
    while(1);
}

void runCommand()
{
    FILE *pipen;
    char *buffer;

    buffer = (char*) malloc(100);
/*sleep #1*/    sleep(5);
    if ((pipen = popen("uname -a", "r")) == NULL) {
        perror("\nError during shell command execution: ");
        exit(0);
    }
/*sleep #2*/    sleep(5);
    fgets(buffer, 100, pipen);
/*sleep #3*/    sleep(5);
    printf("\n%s\n",buffer);
    exit(0);
}

void reaper(int signal)
{
    int status;
    waitpid(-1,&status,WNOHANG);
}

当我运行上述程序时,睡眠#1起作用并且该过程在给定时间内休眠。我通过观察打印命令结果所花费的时间(通过printf语句)来了解这一点。但是对于睡眠#2和睡眠#3,我希望进程能够进入睡眠状态,但由于该命令的结果立即打印在控制台上,因此似乎没有发生。

通过仅保持睡眠#2或睡眠#3呼叫以及移除其他两个睡眠呼叫,可以观察到该行为。

有谁知道为什么会这样?

c signals sleep popen sigchld
1个回答
3
投票

popen()通过分娩一个孩子来执行/bin/sh -c "uname -a"。当该孩子退出时,该过程会收到SIGCHLD信号。这会中断sleep()调用并运行你的reaper函数。

我无法在任何Linux或POSIX文档中找到这个,但它在SunOS documentation中:

使用SIGCHLD时,popen()的信号处理程序应设置为默认值。如果进程已为SIGCHLD建立了信号处理程序,则在命令终止时将调用它。如果信号处理程序或同一进程中的另一个线程发出wait(2)调用,它将干扰pclose()的返回值。如果SIGCHLD的进程信号处理程序已设置为忽略该信号,则pclose()将失败,并且errno将设置为ECHILD

您应该将SIGCHLD处理程序设置回子进程中的默认值。

void runCommand()
{
    FILE *pipen;
    char *buffer;

    signal(SIGCHLD, SIG_DFL);

    buffer = (char*) malloc(100);
/*sleep #1*/    sleep(5);
    if ((pipen = popen("uname -a", "r")) == NULL) {
        perror("\nError during shell command execution: ");
        exit(0);
    }
/*sleep #2*/    sleep(5);
    fgets(buffer, 100, pipen);
/*sleep #3*/    sleep(5);
    printf("\n%s\n",buffer);
    exit(0);
}
© www.soinside.com 2019 - 2024. All rights reserved.