简体   繁体   English

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

[英]How to correctly use pipe to transfer data from child process to parent process?

I'm trying to create a function that returns true if execvp is successful and false if it is not. 我正在尝试创建一个函数,如果execvp成功,则返回true,否则返回false。 Initially, I didn't use a pipe and the problem was that whenever execvp failed, I get 2 returns, one false and one true (from the parent). 最初,我没有使用管道,问题是,每当execvp失败时,我都会从父级获得2个返回,一个为false,一个为true。 Now that I'm piping, I'm never getting a false returned when execvp fails. 现在,我正在执行管道操作,当execvp失败时,我再也不会收到错误的返回。

I know there are a lot related questions and answers on this topic, but I can't seem to narrow down where my particular error is. 我知道与此主题相关的问题和答案很多,但是我似乎无法缩小我的特定错误的位置。 What I want is for my variables return_type_child, return_type_parent, and this->return_type to all contain the same value. 我想要的是我的变量return_type_child,return_type_parent和this-> return_type都包含相同的值。 I expected that in the child process, execvp would fail so the next lines would execute. 我希望在子进程中execvp会失败,因此下一行将执行。 As a result, I thought that the 3 variables mentioned would all be false, but instead when I print out the value in this->return_type, 1 is displayed. 结果,我认为提到的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;
}

I've also tried putting a cout statement after execvp(argv[0], argv), which never ran. 我还尝试过将cout语句放在execvp(argv [0],argv)之后,该语句永远不会运行。 Any help is greatly appreciated! 任何帮助是极大的赞赏!

From the code, it seems to be an XY problem (edit: moved this section to the front due to a comment that confirms this). 从代码来看,这似乎是一个XY问题 (编辑:由于有确认这一点的注释,因此将本节移至最前面)。 If the goal is to get the exit status of the child, then for that there is the value that wait returns , and no pipes are required: 如果目标是获取子项的退出状态,则为此有wait返回的值,并且不需要管道:

int stat;
wait(&stat);

Read the manual of wait to figure out how to read it. 阅读wait手册,以了解如何阅读。 The value of stat can be tested as follows: stat的值可以按以下方式进行测试:

  • WEXITSTATUS(stat) - If WIFEXITED(stat) != 0, then this are the lower 8 bits of child's call to exit(N) or the return value from main . WEXITSTATUS(stat)-如果WIFEXITED(stat)!= 0,则这是孩子对exit(N)的调用的低8位或main的返回值。 It might work correctly without checking WIFEXITED, but the standard does not specify that. 如果不检查WIFEXITED,它可能会正常工作,但是标准未对此进行指定。
  • WTERMSIG(stat) - If WIFSIGNALED(stat) != 0, then this is the signal number that caused the process to exit (eg 11 is segmentation fault). WTERMSIG(stat)-如果WIFSIGNALED(stat)!= 0,则这是导致进程退出的信号编号(例如11是分段错误)。 It might work correctly without checking WIFSIGNALED, but the standard does not specify that. 如果不检查WIFSIGNALED,它可能会正常工作,但是标准未对此进行指定。

There are several errors in the code. 代码中有几个错误。 See the added comments: 查看添加的注释:

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.

This wait is a potential deadlock: if the child writes enough data to the pipe (usually in the kilobytes) , the write blocks and waits for the parent to read the pipe. 这种wait可能会导致死锁:如果子进程将足够的数据写入管道(通常以千字节为单位) ,则写入将阻塞并等待父进程读取管道。 The parent wait(NULL) waits for the child to complete, which which waits for the parent to read the pipe. 父级wait(NULL)等待子级完成,子级等待父级读取管道。 This is likely not effecting the code in question, but it is problematic. 这可能不会影响所讨论的代码,但这是有问题的。

        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)

This is problematic since: 这是有问题的,因为:

  1. the code just lost the original stdin. 代码只是丢失了原始的stdin。
  2. The pipe is never closed. 管道永远不会关闭。 You should close fd[0] explicitly, don't close(0), and don't duplicate fd[0]. 您应该显式关闭fd [0],不要关闭(0),也不要复制fd [0]。
  3. It is good idea to avoid having duplicate descriptors, except for having stderr duplicate stdout. 最好避免具有重复的描述符,除了具有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)

This is problematic, since there are two duplicate data intakes to the pipe. 这是有问题的,因为管道有两个重复的数据输入。 In this case it is not critical since they are both closed automatically when the process ends, but it is a bad practice. 在这种情况下,这并不重要,因为在过程结束时,它们都会自动关闭,但这是一个坏习惯。 It is a bad practice, since only closing all the pipe intakes signals END-OF-FILE to the exhaust. 这是一种不好的做法,因为仅关闭所有进气管的进气口信号END-OF-FILE。 Closing one intake but not the other, does not signal END-OF-FILE. 关闭一个进气口但不关闭另一个进气口,并不表示文件结束。 Again, in your case it is not causing trouble since the child's exit closes all the intakes. 同样,在您的情况下,这不会造成麻烦,因为孩子的出口关闭了所有入口。

        execvp(argv[0], argv);

The code below the above line is never reached, unless execvp itself failed. 除非execvp本身失败,否则永远不会到达上一行下面的代码。 The execvp fails only when the file does not exist, or the caller has no permission to execute it. 仅当文件不存在或调用者没有执行该文件的权限时, execvp才会失败。 If the executable starts to execute and fails later (possibly even if it fails to read a shared library), then still execvp itself succeeds and never returns. 如果可执行文件开始执行并在以后失败(可能即使它无法读取共享库),那么execvp本身仍然成功并且永不返回。 This is because execvp replaces the executable, and the following code is no longer in memory when execvp starts to run the other program. 这是因为execvp替换了可执行文件,并且当execvp开始运行其他程序时,以下代码不再在内存中。

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

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

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