c - 为什么我的管道不能互相通信?

标签 c pipe

我正在尝试用 c 语言编写一个简单的 shell。现在我正在努力让管道正常工作。我有一个 struct c ,我将其输入到该函数中,它包含一个存储管道文件描述符 pipfd 的位置,并且还包含有关每个命令的结束标记的信息c->type(可以是 | || && & 等)。 CommandPrev 只是跟踪最后一个命令,以便我可以查看之前的命令是否有管道标记。

完成此函数后,我将子进程 pid(返回值)交给 waitpid 以等待我使用 execvp 调用的命令

当我运行诸如 echo foo | 之类的命令时echo bar 我得到 bar 作为输出,完全符合我的预期,一切都很好。我的问题是,当我尝试运行任何实际使用管道前半部分输入的命令时,一切都会卡住。如果我运行类似 echo foo | 的东西wc -c 我没有得到任何输出,它只是永远挂起。

我可以看到该函数完成了此类命令,因为我在它返回时进行了打印。发生的情况是我用 execvp 调用的命令从未发生,所以我的 waitpid 永远等待。

我认为管道两端之间的连接不知何故断开了。要么东西永远不会被写入,要么它们永远不会被读取,或者管道的接收端永远不会意识到写入端已完成并且只是永远等待。我立即关闭所有管道,因此我倾向于怀疑它是最后一个......但我真的不确定如何去测试这三个场景中的任何一个。

这是我的代码:

pid_t start_command(command* c, pid_t pgid) {
    (void) pgid;

    // If its a pipe token, create a shared pipe descriptor
    if (c->type == TOKEN_PIPE){
        pipe(c->pipefd);
    }

    // Fork a child process, run the command using `execvp`
    pid_t child = fork();
    if (child == 0) {
        // writing side of the pipe
        if (c->type == TOKEN_PIPE){
            dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
            close(c->pipefd);
        }
        // receiving side of the pipe
        else if (commandPrev->type == TOKEN_PIPE){
            dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
            close(commandPrev->pipefd);
        }

        // run the command
        if (execvp(c->argv[0], c->argv) == -1) {
            // fork failed
            exit(-1);
        }
    } 
    else{
        // clean up, clean up, everybody, everywhere
        if (commandPrev->type == TOKEN_PIPE){
            close(commandPrev->pipefd);
        }
    }
    printf("return %i\n", getpid() );
    return child;
}

谢谢!

最佳答案

正如其他评论者所说,您看起来像是在尝试关闭一个数组。 像这样的东西应该效果更好:

// writing side of the pipe
if (c->type == TOKEN_PIPE){
    close(c->pipefd[READ_SIDE]);
    dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
    close(c->pipefd[WRITE_SIDE]);
}
// receiving side of the pipe
if (commandPrev->type == TOKEN_PIPE){
    close(commandPrev->pipefd[WRITE_SIDE]);
    dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
    close(commandPrev->pipefd[READ_SIDE]);
}

或者,您可以在父级中调用 waitpid 后关闭管道的事件端。像这样的事情:

waitpid(child, &status, 0);

if (commandPrev->type == TOKEN_PIPE){
    close(commandPrev->pipefd[READ_SIDE]);
}
if (c->type == TOKEN_PIPE){
    close(c->pipefd[WRITE_SIDE]);
}

关于c - 为什么我的管道不能互相通信?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40701808/

相关文章:

c - L值到R值的转换

c - 处理SIGCHLD,如何记录 child 死亡时的返回值

c - 如何使用 dup 和/或 dup2 将标准输出重定向到管道,然后输出到另一个管道,然后再返回到标准输出?

node.js - 如何正确地 unpipe nodejs 流

c - 正确的 fork() 和 pipe() 用于有多个 child 的单亲。我该怎么做?

c - 在推出自己的结构时提供辅助功能

c - 为什么会发生段错误以及我们如何避免这种情况?

c++ - 为什么用 C 复制文件比 C++ 快得多?

unix - 将 unix 管道重新连接到子进程

c - 通过管道在 C 中进行实时流 gpg 加密