fork()未按预期工作

fork() not working as expected

本文关键字:工作 fork      更新时间:2023-10-16

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

#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炸弹。。。您的函数调用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()进程是监视器进程的子进程,这是应该的。

在makeSibling函数中,在第二个if块的末尾添加一个exit(0);或者什么的。因为你实际上是在轰炸。在使用fork时,您必须在大多数时候考虑该出口。

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

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

一方面,你们实际上是在创造过程而不杀死他们。

另一方面,如果你从另一个终端发送的信号不是杀死,而是破坏了无限循环,那么第一个同级也会执行第二个fork。