【问题标题】:fork() not working as expectedfork() 没有按预期工作
【发布时间】:2015-02-13 08:32:04
【问题描述】:

我正在尝试创建一个生成两个进程的程序——第一个进程监视第二个进程,如果它被杀死则重新启动它。 (想法是杀死第二个进程的唯一方法是首先杀死第一个。)下面我的代码无法实现我的目标。

#include <cstdio>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <signal.h>
#include <cerrno>

void makeSiblings();

void run_ping() {

  while (true) {

    std::this_thread::sleep_for(std::chrono::seconds(2));
    puts("Ping.");
  }
}

void run_monitor(pid_t sibling) {

  while (kill(sibling, 0) != -1 and errno != ESRCH) {

    printf("%d still exists.\n", sibling);
    std::this_thread::sleep_for(std::chrono::seconds(1));
  }

  makeSiblings();
}

void makeSiblings() {

  pid_t pid1, pid2;

  if ((pid1 = fork()) == -1) {

    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (pid1 == 0) {

    setsid();
    run_ping();
  }

  if ((pid2 = fork()) == -1) {

    perror("fork");
    exit(EXIT_FAILURE);
  }

  if (pid2 == 0) {

    setsid();
    run_monitor(pid1);
  }
}

int main() {

  makeSiblings();
}

运行此代码时,它会输出 Ping. 和例如7812 still exists. 分别每两秒和一秒 - 正如预期的那样。然而,当我从另一个终端 kill 7812 时,“Ping”过程激增到一个荒谬的程度——我似乎正在对自己进行分叉轰炸,只有通过发送垃圾邮件 killall 我才能恢复。

我似乎无法理解fork()setsid() 的一些微妙之处。我会很感激一两个指针。

【问题讨论】:

  • 这显然是 C++,所以我删除了 'C' 标签。
  • 它是 C++,是的,但问题是 C 程序员应该可以解决的问题——我用 C++ 编写它的唯一原因是利用 sleep_for() 函数。尽管如此,我还是推迟了。
  • 你应该测试setsid的失败。您应该使用调试器。不要忘记在fork-ing 之前刷新流。尝试strace你的程序。
  • 并且监视器应该是被监视程序的父进程。你不能用SIGKILL来禁止进程被杀死
  • 我认为删除C标签是个错误..

标签: c++ linux process fork


【解决方案1】:

是的,你是 fork bombimg...你的函数调用 fork() 两次,然后两个孩子最终都会递归调用 makeSiblings()

当第一个子进程调用run_ping() 结束时,它实际上并没有结束,但它会调用run_monitor()。然后它会调用makeSiblings() 并重复直到它爆炸!

解决方案是在run_ping() 之后添加一个exit() 调用:

if (pid1 == 0) {
    setsid();
    run_ping();
    exit(EXIT_SUCCESS); // <------ here!
}

另请注意,您正在 makeSiblings()run_monitor() 之间进行尾递归,这可能不是一个好主意,因为您的堆栈可能会溢出。

我会写这样的:

void run_monitor(pid_t sibling) {
  while (kill(sibling, 0) != -1 and errno != ESRCH) {
      //...
  }
}

void makeSiblings() {
  pid_t pid2 = fork(); //error code ommited

  setsid();
  if (pid2 != 0)
      return;

  //this is now the monitor process
  while (true) {
      pid_t pid1 = fork(); //error code ommited
      if (pid1 == 0) {
        setsid();
        run_ping();
        exit(EXIT_SUCCESS);
      }
      run_monitor(pid1);
  }
}

但现在,您只需使用 wait() 而不是 kill(pid, 0),因为 ping() 进程是监视器的子进程,应该是。

【讨论】:

  • 好收获!另一种解决方案是改用 if-then-else
  • @malarres: 是的,但我认为 OP 程序可能更复杂,并且在main()makeSiblings() 之后的代码更多。在此示例中,使用return 而不是exit(0) 可能是等效的,但在更复杂的代码中则不然。
  • 这实际上不是真的;我发布了我拥有的所有代码。谢谢你的建议!
  • 我现在明白我做错了什么。谢谢!接受这个答案。
  • @Arandur:YW。一个额外的建议。从多进程应用程序执行 printf-debugging 时,在每一行添加写入进程的 pid。这将使事情更容易理解。
【解决方案2】:

在 makeSibling 函数中,在第二个 if 块的末尾,添加一个 exit(0);或者其他的东西。因为你实际上是分叉轰炸。 使用 fork 时,您必须在大多数情况下考虑退出。

【讨论】:

    【解决方案3】:

    ... int kill(pid_t pid, int sig); ... 如果 sig 为 0(空信号), 执行错误检查,但实际上没有发送信号。空 信号可以用来检查pid的有效性。 ...

    来源:http://linux.die.net/man/3/kill

    一方面,您实际上是在创建进程而不杀死它们。

    另一方面,如果你从另一个终端发送的信号不是杀死而是打破无限循环,第一个兄弟也会执行第二个分叉。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-19
      • 2020-03-18
      • 2012-06-14
      • 2014-11-15
      • 1970-01-01
      • 2012-07-02
      • 2011-09-07
      • 2013-03-03
      相关资源
      最近更新 更多