在 system() 内部正确处理信号

Treating signals correctly inside system()

我一直在阅读"The Linux Programming Interface"。第 27 章,程序执行。

我了解到作者演示了我们如何使用 execfork 实现 system 调用。然而,具有挑战性的部分是处理信号。特别是我对以下文字感到困惑

The first signal to consider is SIGCHLD. Suppose that the program calling system() is also directly creating children, and has established a handler for SIGCHLD that performs its own wait(). In this situation, when a SIGCHLD signal is generated by the termination of the child created by system(), it is possible that the signal handler of the main program will be invoked and collect the child’s status before system() has a chance to call waitpid(). (This is an example of a race condition.)

以下是没有信号处理的代码示例

#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

int system(char *command)
{
  int status;
  pid_t childPid;

  switch(childPid = fork())
  {
    case -1: /* Error */
      return -1;

    case 0: /* Child */
      execl("/bin/sh", "sh", "-c", command, (char*) NULL);
      _exit(127); /* If reached this line than execl failed*/

    default: /* Parent */
      if (waitpid(childPid), &status, 0) == -1)
        return -1;
      else
        return status;
  }
}

我知道什么是竞争条件主义,但不理解作者描述的整个场景。特别是,我不明白 "the program calling system" 可能是什么。 "main program" 是什么?哪个进程创建子进程?

有人可以举例说明竞争条件是如何产生的吗?在 C 或伪代码中。

您可以安装一个 SIGCHLD 处理程序来执行 int ws; wait(&ws);

如果允许这样的 SIGCHLD 处理程序 运行 响应 SIGCHLD,它将与 system 中完成的 waitpid 竞争,如果处理程序赢得比赛,则阻止 system 成功检索 child 的退出状态。

因此,POSIX 规定 SIGCHLDsystem 中被阻止。

您仍然可以与在其他信号处理程序或其他线程中完成的 wait 调用竞争,但这将是一个设计错误,POSIX 系统不会帮助您。

#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>

int system(char *command)
{
  int status;
  pid_t childPid;

  switch(childPid = fork())
  {
    case -1: /* Error */
      return -1;

    case 0: /* Child */
      execl("/bin/sh", "sh", "-c", command, (char*) NULL);
      _exit(127); /* If reached this line than execl failed*/

    default: /* Parent */
      /*usleep(1);*/
      if (waitpid(childPid, &status, 0) == -1)
        return -1;
      else
        return status;
  }
}
void sigchld(int Sig){ int er=errno; wait(0); errno=er; }
int main()
{
    /*main program*/

    //main program has a sigchld handler
    struct sigaction sa; 
    sa.sa_flags = 0;
    sigemptyset(&sa.sa_mask);
    sa.sa_handler = sigchld;
    sigaction(SIGCHLD, &sa,0);

    for(;;){
        //the handler may occasionally steal the child status
        if(0>system("true") && errno==ECHILD)
            puts("Child status stolen!");

    }

}