繁体   English   中英

意外的分叉行为

[英]Unexpected fork behavior

我有一个可以无限期运行的程序。 为了进行测试,我制作了一个包装程序,该程序在指定的时间(通过命令行/终端参数指定)后杀死另一个程序。 被分叉的程序要求将其传递给两个具有相同名称的文件夹(我对此无能为力),因此我只需将两次相同的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

作为参数传递的数组应为空终止。 例如:

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

无论如何,如果您想要的只是一个简单的包装程序,该包装程序运行一个带有超时的子进程,那么只要您愿意从timeout参数开始,您的程序就可以非常简单和通用:

/*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其关闭) ulimit -c unlimited 在运行主进程之前先运行它。 (尽管您可以,但我会很乐意将其运行在fork中。)

当程序崩溃时,将生成一个核心转储,您可以使用gdb进行检查。

要获取核心文件的帮助,您可以将其谷歌搜索。

除此之外。 您可以创建一个脚本来启动文件。 您可以使用脚本来记录内容。

你要:

char* test[3];
test[0] = argv[2];
test[1] = argv[2];
test[2] = NULL;

您需要一个NULL参数来标记参数列表的末尾。

给定规格:

该命令的形式完全是:

 <executable> <wrapped prog> <folder> <duration> 

在相对路径中:

 Intel/debug/Tester.exe <program> test 10 

并且:

分叉的程序要求将其传递给两个具有相同名称的文件夹…

然后,假设您已检查包装器是否传递了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()

请注意,此代码可确保控制流不会从应该代表子代的语句块中逸出。 随后让子进程继续进行,通常实际上是认为它是父进程,这会导致混乱。 始终确保孩子执行或退出。 (这是夸夸其谈的说法-是的;但是这个想法背后也有很多道理。)而且,由于这是C ++,因此您可能需要考虑如何结束C ++代码? 这使生活变得复杂。 至关重要的是,如果子进程无法执行,它将不会像父进程一样继续执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM