我正在尝试编写一个可自行重启一次的C程序。我的方法是转到fork
,并在子进程调用中execve
。我以为我会看到两次打印Hello
。这在这里打印两次main
,但是即使我注释掉了execve
,我也认为我在这里没有正确使用execve
。该二进制文件称为“ restartOnce”。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
int main(int argc, char *argv[]) {
pid_t pid = fork();
printf("Main.\n");
if (pid == 0) {
char *argv = "hello"; // To silence gcc compiler warning.
char *env = "world"; // To silence gcc compiler warning.
execve("restartOnce", &argv, &env);
} else {
wait(NULL);
printf("Done.\n");
}
return 0;
}
我不确定您要完成什么,但是我看到有两点要注意。我们必须正确执行execve()
调用,此外,我们还必须避免产生叉式炸弹,并且此代码应该可以实现。
有很多避免叉形炸弹的方法,但是我选择在环境中设置一些东西,当孩子看到此环境变量时,它将知道不会继续。
所以execve()
需要它自己的argv
,它是指向各个字符串的指针的数组(末尾带有NULL指针,并且完全可以传递从main获得的相同参数。)>
第三个参数类似于argv
,但它是环境变量列表,而不是命令行参数。它具有TERM=vt100
,PATH=/bin/...
等许多其他内容。尽管您可以使用getenv("PATH")
单独获取环境变量,但Unix / Linux系统提供了类似argv的变量environ
。
此变量在<unistd.h>
中声明,但需要定义_GNU_SOURCE
宏才能公开。
完成后,您可以通过这种方式安全地呼叫execve
:
#define _GNU_SOURCE #include <unistd.h> ... execve("restartOnce", argv, environ);
为了避免产生叉式炸弹,该程序要做的第一件事就是寻找环境变量
FORKBOMB
-如果设置了该变量,则说明我们处于子进程中,应该停止分叉其他子进程,或者做其他事情(该程序的实际工作?)
但是如果我们在父级中-没有看到变量-我们通过将其放在子进程可以看到的自己的环境中来主动set
变量:putenv("FORKBOMB=no");
一个小小的补充:程序要做的第一件事就是报告它已经启动,并且它提供了自己的进程ID,以便您知道它在做什么。
也许我误解了这个问题,但这就是我要解决的我想像中的问题:
#define _GNU_SOURCE // for "environ" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <wait.h> int main(int argc, char *argv[]) { printf("Main in pid %d.\n", getpid() ); if (getenv("FORKBOMB") != 0) { printf("**We're in the second child, no more forking\n"); exit(0); } putenv("FORKBOMB=no"); pid_t pid = fork(); if (pid == 0) { execve("restartOnce", argv, environ); } else { wait(NULL); printf("Done.\n"); } return 0; }
[还有很多其他方法可以避免产生叉式炸弹,但这与任何使想法得以传播的方法一样好,其他方法可能会引起他们的喜爱。
EDIT
但是,考虑到这一点,我很确定无论您想要完成什么,这都是没有必要的,除非这只是一种学习练习。我可以想象的两件事是:
1)您需要与父进程分离,以便可以在后台运行,就像守护进程一样。在这种情况下,您不必执行,只需在子进程中执行守护程序即可。
2)如果程序更改了[[itself
-也许下载了其自身的更新版本,则它确实必须调用exec来获取新的可执行文件,但是不需要fork()
。所以我不确定我们(或至少我)知道您要完成的工作。