意外的分叉行为
Unexpected fork behavior
我有一个无限期运行的程序。出于测试目的,我制作了一个包装程序,它在指定的时间后杀死另一个程序(通过命令 line/terminal args 指定)。被分叉的程序需要传递两个同名的文件夹(我无法控制这个),所以我只是将相同的 arg 传递给它两次,如下所示:
pid_t pid = fork();
if(pid == 0)
{
//build the execution string
char* test[2];
test[0] = argv[2];
test[1] = argv[2];
test[2] = NULL;
cout << "test[0] is " << test[0] << endl;
cout << "test[1] is " << test[1] << endl;
cout << "argv[1] is " << argv[1] << endl;
execvp(argv[1],test);
}
问题是在 argv[1] 中传递的程序不断出现分段错误。如果我通过终端自行调用它,它可以毫无问题地运行。在这两种情况下,我都传递了相同的文件夹。谁能告诉我为什么它对 execvp 不起作用?
我应该提到一个同事运行他的电脑上也是这样,第一次可以正常运行,但之后每次都会出现段错误。
编辑:我添加了一个空术语进行测试,但是,这并没有解决问题。
命令的格式完全是:
<executable> <wrapped prog> <folder> <duration>
在相对路径中是:
Intel/debug/Tester.exe <program> test 10
作为参数传递的数组应该以 null 结尾。例如:
char *test[3]={0};
...
如果数组的长度是静态的,你可能会更好
execlp
execlp(argv[1], argv[1], argv[2], argv[2], (char*)0);
至于execvp
,数组应该以可执行文件的名称开始,以NULL
结束。
execvp
char* args[] = { argv[1], argv[2], argv[2], NULL };
execvp(argv[1], args);
runWithTimeout
无论如何,如果你想要的只是一个简单的包装器,它运行一个带有超时的子进程,那么你的程序可能会非常简单和通用,只要你愿意从超时参数开始:
/*runWithTimeout.c
compile with: make runWithTimeout
run with: ./runWithTimeout seconds program arguments...
*/
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
int main(int argc, char** argv)
{
assert(argc >= 1+2);
int pid, status = 1;
if((pid = fork()) == 0) {
alarm(atoi(argv[1]));
execvp(argv[2], argv + 2);
/*^the child ends here if execvp succeeds,
otherwise fall-through and return the default error status of 1
(once in child (which has no one to wait on) and
then in the parent (which gets the status from the child))*/
perror("Couldn't exec");
}else if(pid < 0){ perror("Couldn't fork"); };
wait(&status);
return status;
}
您可以打开核心转储(确保在完成后将其关闭)ulimit -c unlimited
。 运行 它在你 运行 你的主要进程之前。 (虽然你可能可以,但我会担心 运行 将它放在叉子中。)
当您的程序崩溃时,这将产生一个核心转储,您可以使用 gdb 检查它。
有关核心文件的帮助,您可以 google 它们。
除此之外。您可以制作一个脚本来启动您的文件。您可以使用脚本来记录内容。
你想要:
char* test[3];
test[0] = argv[2];
test[1] = argv[2];
test[2] = NULL;
您需要一个 NULL 参数来标记参数列表的结尾。
给定规范:
The command's form is exactly:
<executable> <wrapped prog> <folder> <duration>
In relative paths it's:
Intel/debug/Tester.exe <program> test 10
还有:
The program being forked requires that it is passed two folders with the same name…
然后,假设您已检查包装器是否传递了 4 个参数,您需要的代码是:
pid_t pid = fork();
if (pid == 0)
{
//build the execution string
char *test[4]; // Note the size!
test[0] = argv[1]; // Program name: argv[0] in exec'd process
test[1] = argv[2]; // Directory name: argv[1] …
test[2] = argv[2]; // Directory name: argv[2] …
test[3] = NULL; // Null terminator
cout << "test[0] is " << test[0] << endl;
cout << "test[1] is " << test[1] << endl;
cout << "test[2] is " << test[2] << endl;
execvp(test[0], test);
cerr << "Failed to exec '" << test[0] << "': " << strerror(errno) << endl;
exit(1); // Or throw an exception, or …
}
除了对 argv
.
中的参数数组使用成语 execvp(argv[0], argv)
之外,很少(但并非从来没有)有理由调用 execvp()
请注意,此代码确保控制流不会从应该表示 child 的语句块中逃逸。让 child 进程在之后继续,通常实际上认为它是一个 parent 进程,这会导致混淆。始终确保 child 执行或退出。 (这是一个修辞 over-statement — 是的;但这个想法背后也有很大一部分道理。)此外,由于这是 C++,您可能需要考虑 。这使生活复杂化。关键是,如果 child 进程无法执行,它不会像 parent 进程那样继续。
我有一个无限期运行的程序。出于测试目的,我制作了一个包装程序,它在指定的时间后杀死另一个程序(通过命令 line/terminal args 指定)。被分叉的程序需要传递两个同名的文件夹(我无法控制这个),所以我只是将相同的 arg 传递给它两次,如下所示:
pid_t pid = fork();
if(pid == 0)
{
//build the execution string
char* test[2];
test[0] = argv[2];
test[1] = argv[2];
test[2] = NULL;
cout << "test[0] is " << test[0] << endl;
cout << "test[1] is " << test[1] << endl;
cout << "argv[1] is " << argv[1] << endl;
execvp(argv[1],test);
}
问题是在 argv[1] 中传递的程序不断出现分段错误。如果我通过终端自行调用它,它可以毫无问题地运行。在这两种情况下,我都传递了相同的文件夹。谁能告诉我为什么它对 execvp 不起作用?
我应该提到一个同事运行他的电脑上也是这样,第一次可以正常运行,但之后每次都会出现段错误。
编辑:我添加了一个空术语进行测试,但是,这并没有解决问题。
命令的格式完全是:
<executable> <wrapped prog> <folder> <duration>
在相对路径中是:
Intel/debug/Tester.exe <program> test 10
作为参数传递的数组应该以 null 结尾。例如:
char *test[3]={0};
...
如果数组的长度是静态的,你可能会更好
execlp
execlp(argv[1], argv[1], argv[2], argv[2], (char*)0);
至于execvp
,数组应该以可执行文件的名称开始,以NULL
结束。
execvp
char* args[] = { argv[1], argv[2], argv[2], NULL };
execvp(argv[1], args);
runWithTimeout
无论如何,如果你想要的只是一个简单的包装器,它运行一个带有超时的子进程,那么你的程序可能会非常简单和通用,只要你愿意从超时参数开始:
/*runWithTimeout.c
compile with: make runWithTimeout
run with: ./runWithTimeout seconds program arguments...
*/
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
int main(int argc, char** argv)
{
assert(argc >= 1+2);
int pid, status = 1;
if((pid = fork()) == 0) {
alarm(atoi(argv[1]));
execvp(argv[2], argv + 2);
/*^the child ends here if execvp succeeds,
otherwise fall-through and return the default error status of 1
(once in child (which has no one to wait on) and
then in the parent (which gets the status from the child))*/
perror("Couldn't exec");
}else if(pid < 0){ perror("Couldn't fork"); };
wait(&status);
return status;
}
您可以打开核心转储(确保在完成后将其关闭)ulimit -c unlimited
。 运行 它在你 运行 你的主要进程之前。 (虽然你可能可以,但我会担心 运行 将它放在叉子中。)
当您的程序崩溃时,这将产生一个核心转储,您可以使用 gdb 检查它。
有关核心文件的帮助,您可以 google 它们。
除此之外。您可以制作一个脚本来启动您的文件。您可以使用脚本来记录内容。
你想要:
char* test[3];
test[0] = argv[2];
test[1] = argv[2];
test[2] = NULL;
您需要一个 NULL 参数来标记参数列表的结尾。
给定规范:
The command's form is exactly:
<executable> <wrapped prog> <folder> <duration>
In relative paths it's:
Intel/debug/Tester.exe <program> test 10
还有:
The program being forked requires that it is passed two folders with the same name…
然后,假设您已检查包装器是否传递了 4 个参数,您需要的代码是:
pid_t pid = fork();
if (pid == 0)
{
//build the execution string
char *test[4]; // Note the size!
test[0] = argv[1]; // Program name: argv[0] in exec'd process
test[1] = argv[2]; // Directory name: argv[1] …
test[2] = argv[2]; // Directory name: argv[2] …
test[3] = NULL; // Null terminator
cout << "test[0] is " << test[0] << endl;
cout << "test[1] is " << test[1] << endl;
cout << "test[2] is " << test[2] << endl;
execvp(test[0], test);
cerr << "Failed to exec '" << test[0] << "': " << strerror(errno) << endl;
exit(1); // Or throw an exception, or …
}
除了对 argv
.
execvp(argv[0], argv)
之外,很少(但并非从来没有)有理由调用 execvp()
请注意,此代码确保控制流不会从应该表示 child 的语句块中逃逸。让 child 进程在之后继续,通常实际上认为它是一个 parent 进程,这会导致混淆。始终确保 child 执行或退出。 (这是一个修辞 over-statement — 是的;但这个想法背后也有很大一部分道理。)此外,由于这是 C++,您可能需要考虑