fork()没有按预期工作

时间:2015-02-13 08:32:04

标签: c++ linux process fork

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

#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()的一些微妙之处。我会很感激一两个指针。

3 个答案:

答案 0 :(得分:2)

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

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

解决方案是在exit()之后添加run_ping()来电:

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

  

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

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

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

另一方面,如果你从另一个终端发送的信号没有被杀死但只是打破了无限循环,那么第一个兄弟也将执行第二个分支。