如何正确使用管道将数据从子流程传输到父流程?

时间:2019-02-17 02:20:14

标签: c++ fork execvp

我正在尝试创建一个函数,如果execvp成功,则返回true,否则返回false。最初,我没有使用管道,问题是,每当execvp失败时,我都会从父级得到2个返回,一个为false,一个为true。现在,我正在执行管道操作,当execvp失败时,我再也不会收到错误的返回。

我知道与此主题相关的问题和答案很多,但是我似乎无法缩小我的特定错误的位置。我想要的是我的变量return_type_child,return_type_parent和this-> return_type都包含相同的值。我希望在子进程中execvp会失败,因此下一行将执行。结果,我认为提到的3个变量都为假,但是当我打印出this-> return_type中的值时,将显示1。

bool Command::execute() {
    this->fork_helper();
    return return_type;
}

void Command::fork_helper() {
    bool return_type_child = true;
    int fd[2];
    pipe(fd);
    pid_t child;
    char *const argv[] = {"zf","-la", nullptr};
    child = fork();
    if (child > 0) {
        wait(NULL);
        close(0);
        close(fd[1]);
        dup(fd[0]);
        bool return_type_parent = read(fd[0], &return_type_child, sizeof(return_
        this->return_type = return_type_parent;
    }
    else if (child == 0) {
        close(fd[0]);
        close(1);
        dup(fd[1]);
        execvp(argv[0], argv);
        this->return_type = false;
        return_type_child = false;
        write(1,&return_type_child,sizeof(return_type_child));
    }
    return;
}

我还尝试将cout语句放在从未运行过的execvp(argv [0],argv)之后。任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

从代码中看,它似乎是XY problem(编辑:由于有确认这一点的注释,因此将本节移至最前面)。如果目标是获取孩子的退出状态,则为此的值为wait returns,并且不需要管道:

int stat;
wait(&stat);

阅读wait的手册以了解如何阅读。可以如下测试stat的值:

  • WEXITSTATUS(stat)-如果WIFEXITED(stat)!= 0,则这是孩子对exit(N)的调用的低8位或main的返回值。无需检查WIFEXITED,它可能可以正常工作,但是标准未对此进行指定。
  • WTERMSIG(stat)-如果WIFSIGNALED(stat)!= 0,则这是导致进程退出的信号编号(例如11是分段错误)。如果不检查WIFSIGNALED,它可能会正常工作,但标准未对此进行指定。

代码中有几个错误。查看添加的评论:

void Command::fork_helper() {
    // File descriptors here: 0=stdin, 1=stdout, 2=stderr 
    //(and 3..N opened in the program, could also be none).
    bool return_type_child = true;
    int fd[2];
    pipe(fd);
    // File descriptors here: 0=stdin, 1=stdout, 2=stderr 
    //(and 3..N opened in the program, could also be none).
    // N+1=fd[0] data exhaust of the pipe
    // N+2=fd[1] data intake of the pipe
    pid_t child;
    char *const argv[] = {"zf","-la", nullptr};
    child = fork();
    if (child > 0) {
        // This code is executed in the parent.
        wait(NULL); // wait for the child to complete.

wait是潜在的死锁:如果子项writes enough data to the pipe (usually in the kilobytes),写操作将阻塞并等待父项读取管道。父级wait(NULL)等待子级完成,子级等待父级读取管道。这可能不会影响所讨论的代码,但这是有问题的。

        close(0);
        close(fd[1]);
        dup(fd[0]);
        // File descriptors here: 0=new stdin=data exhaust of the pipe
        // 1=stdout, 2=stderr 
        // (and 3..N opened in the program, could also be none).
        // N+1=fd[0] data exhaust of the pipe (stdin is now a duplicate)

这是有问题的,因为:

  1. 代码刚刚丢失了原始的stdin。
  2. 管道永远不会关闭。您应该显式关闭fd [0],而不要关闭(0), 并且不要复制fd [0]。
  3. 除了具有stderr重复的stdout之外,最好避免具有重复的描述符。

        bool return_type_parent = read(fd[0], &return_type_child, sizeof(return_
        this->return_type = return_type_parent;

    }
    else if (child == 0) {
        // this code runs in the child.
        close(fd[0]);
        close(1);
        dup(fd[1]);
        // File descriptors here: 0=stdin, 1=new stdout=pipe intake, 2=stderr 
        //(and 3..N opened in the program, could also be none).
        // N+2=fd[1] pipe intake (new stdout is a duplicate)

这是有问题的,因为管道有两个重复的数据输入。在这种情况下,这并不重要,因为在过程结束时,它们都会自动关闭,但这是一个坏习惯。这是一种不好的做法,因为仅关闭所有进气管的进气口信号END-OF-FILE。关闭一个进气口但不关闭另一个进气口,并不表示文件结束。同样,在您的情况下,这不会造成麻烦,因为孩子的出口关闭了所有入口。

        execvp(argv[0], argv);

除非execvp本身失败,否则永远不会到达上一行下面的代码。 execvp仅在文件不存在或调用者没有执行权限时失败。如果可执行文件开始执行并在以后失败(可能即使无法读取共享库也是如此),则execvp本身仍然成功并且永不返回。这是因为execvp替换了可执行文件,并且execvp开始运行另一个程序时,以下代码不再在内存中。

        this->return_type = false;
        return_type_child = false;
        write(1,&return_type_child,sizeof(return_type_child));
    }
    return;
 }
相关问题