fork() 没有按预期工作

fork() not working as expected

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

#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() 的一些微妙之处。我将不胜感激。

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

当调用run_ping()的第一个child完成时,它实际上并没有完成,但它会调用run_monitor()。然后它会调用makeSiblings()并重复直到它爆炸!

解决方案是在 run_ping():

之后添加一个 exit() 调用
if (pid1 == 0) {
    setsid();
    run_ping();
    exit(EXIT_SUCCESS); // <------ here!
}

另请注意,您在 makeSiblings()run_monitor() 之间执行 tail-recursion,这可能不是一个好主意,因为您的堆栈可能会溢出。

我会这样写:

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() 进程是监视器进程的 child,它应该是。

在 makeSibling 函数中,在第二个 if 块的末尾,添加一个 exit(0);或者其他的东西。因为你实际上是叉子轰炸。 在使用fork的时候,很多时候一定要想到那个exit

... int kill(pid_t pid, int sig); ... If sig is 0 (the null signal), error checking is performed but no signal is actually sent. The null signal can be used to check the validity of pid. ...

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

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

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