在为我的 OS 类编写程序时,我发现了一个有趣的案例,其中涉及一个似乎涉及 setpgid 的竞争条件。

分别编译下面的每个程序。执行完./test 3(或任意大于2的数字)后,ps jx会显示所有的infy进程都被放到了同一个组中。 ./test 2 将显示 setpgid 尝试移动最后一个进程失败的错误。取消注释“修复我”行将导致 ./test 2 按预期工作。

任何人都可以提供解释或解决方案吗?

// test.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

char* args[] = {
  "./infy",
  NULL
};

int main(int argc, char* argv[])
{
  if (argc != 2)
  {
    fprintf(stderr, "Usage: %s [num]\n", argv[0]);
    return 1;
  }
  int num = strtol(argv[1], NULL, 10);
  if (num < 2)
  {
    fprintf(stderr, "Invalid number of processes\n");
    return 1;
  }

  pid_t pid = fork();
  if (pid > 0)
  {
    int s;
    waitpid(pid, &s, 0);
    fprintf(stderr, "Children done\n");
  }
  else
  {
    pid_t pgid = -1;
    int i;
    for (i = 1; i < num; i++)
    {
      pid_t pid2 = fork();
      if (pid2 > 0)
      {
        if (pgid == -1)
        {
          pgid = pid2;
        }
      }
      else
      {
        if (setpgid(0, pgid == -1 ? 0 : pgid) != 0)
        {
          perror("setpgid failed in non-last process");
        }
        execve(args[0], args, NULL);
        perror("exec failed");
        exit(1);
      }
    }

    // uncomment me to fix
    //fprintf(stderr, "pgid %d\n", pgid);
    if (setpgid(0, pgid) != 0)
    {
      perror("setpgid failed in last process");
    }
    execve(args[0], args, NULL);
    perror("exec failed");
    exit(1);
  }
}

其中“infy”是一个单独的程序:
// infy.c
#include <unistd.h>

int main()
{
  while (1)
  {
    sleep(1);
  }
}

最佳答案

您的问题的答案似乎在 setpgid(2) 的手册页中给出:

ERRORS
       EACCES An attempt was made to change the process group ID of
              one of the children of the calling process and the child
              had  already  performed an execve(2) (setpgid(),
              setpgrp()).

这是一个竞争条件。如果您的原始父进程(最终在您的注释下方运行 setpgid() 调用的进程)设法在其子进程 execve() 执行另一个可执行文件之前执行它,它将成功。如果子进程在父进程到达 setpgid() 之前设法执行 execve(),则父进程 setpgid() 将失败。

缺少 fprintf() 调用最终会更改父进程的执行配置文件,并对其进行足够的更改,从而最终影响父进程赢得或输掉比赛的可能性。

我觉得很有趣的是,额外的 fprintf() 调用似乎实际上使您的父进程赢得了比赛!但是它就是这样啊。

关于c - 与 setpgid 竞争条件,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28732764/

10-12 00:42