【问题标题】:Sleep not working in child process after the popen function call在 popen 函数调用后睡眠在子进程中不起作用
【发布时间】:2019-09-05 21:29:15
【问题描述】:

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

相反,如果我将 sleep 调用放在 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);
}

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

可以通过仅保留 sleep #2 或 sleep #3 调用并移除其他两个 sleep 调用来观察行为。

有人知道为什么会这样吗?

【问题讨论】:

  • 你怎么知道哪个睡眠被忽略了?它只打印一次,一切都完成后。如果不到 15 秒,则无法判断跳过了哪一个。
  • 我正在评论两个睡眠呼叫,并且在运行时一次只保留一个。
  • 更好的方法是在每次睡眠后拨打printf()
  • 当我测试它时,忽略睡眠 #2。
  • 我按照您的建议在每次睡眠前添加了 printfs,您是对的。它只忽略了睡眠 #2。但是当我删除 sleep #1 和 sleep #2 时,它忽略了 sleep #3。

标签: c signals sleep popen sigchld


【解决方案1】:

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);
}

【讨论】:

  • 好像popen创建的子进程在popen调用完成后退出了。那么 pclose 调用有什么作用呢?
  • @pranav 它调用waitpid() 来获取进程的退出状态,然后将其作为自己的值返回。在您调用pclose() 之前,子进程将是一个僵尸进程。但如果你有自己的SIGCHLD 处理程序来获取进程,pclose() 将无法做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-23
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
  • 2012-08-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多