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