管道的一端是否同时具有读和写 fd?

管道的一端是否同时具有读和写 fd?

据我了解,管道的一端既可以读写 fd,另一端也可以读写 fd。这就是为什么当我们使用 using 编写时fd[1],我们关闭fd[0]管道同一侧的读取端,而当我们使用 using 从第二端读取时,fd[0]我们关闭fd[1]该端的读取端。我对么?

答案1

是的,用 pipeline() 创建的管道有两个文件描述符。fd[0]用于阅读和fd[1]写作。

不,您不必关闭管道的任一端,它可以用于双向通信。

编辑:在评论中你想知道这与 有何关系ls | less,所以我也会解释一下:

您的 shell 有三个打开的​​文件描述符:0 (stdin)、1 (stdout) 和 2 (stderr)。当 shell 执行命令时,它会执行类似的操作(我对其进行了一些简化:

pid = fork();
if(pid == 0) {
    /* I am the child */
    execve(...); /* Whatever the user asked for */
}
else {
    waitpid(pid); /* Wait for child to complete */
}

文件描述符 0、1 和 2 由子进程继承,因此输入/输出按预期工作。如果你这样做ls | less,重定向时会发生一些稍微不同的事情:

int pipe[2];
pipe(pipe, 0);

pid1 = fork();
if(pid1 == 0) {
    /* This is ls, we need to remap stdout to the pipe. We don't care about reading from the pipe */
    close(pipe[0]);
    close(1);
    dup2(pipe[1], 1);
    execve(...);
}
else {
    pid2 = fork();
    if(pid2 == 0) {
        /* This is less, it reads from the pipe */
        close(pipe[1]);
        close(0);
        dup2(pipe[0], 0);
        execve(...);
    }
    else {
        waitpid(pid1);
        waitpid(pid2);
    }
}

因此,shell 创建管道、分叉,并在执行之前将管道重新映射到子进程的 stdin 或 stdout,使数据从进程 1 流向进程 2。由于 shell 管道不是双向的,因此它们仅使用管道的一端管道并关闭另一端(在将文件描述符复制到标准输入或标准输出后,它实际上也关闭了另一端)。

答案2

这是一个总结聊天对话 丹尼斯我已经简化了,所以其他新手也可以使用它。执行时ls | less

  1. bash shell 是具有fd[0]fd[1]、 和 的父进程fd[2]
  2. pipe()由父级调用,它创建一个管道,该管道只不过是一个容纳数据的缓冲区,并且它的每一端都有一个文件描述符,一个用于读取,一个用于写入。
  3. 子进程继承所有打开的fd; 0、1 和 2,以及管道的两个。因此,和ls都有自己的缓冲区副本,但它们都指向来自 的相同管道 fds 。在第一个子进程内执行,现在我们需要将输出重新映射到管道 ( ) 的写入端,然后映射到控制台。因此并将进行重新映射。只影响子进程,不影响父进程。现在, 的输出将定向到子级管道的写入端。仍然是读取端,我们不希望数据自己读取,所以我们仍然需要用 关闭该端。同样,这仅关闭 中的文件描述符,而不是.fd[0]fd[1]bashlsfd[1]close(1)dup(fd[1])close(1)lsbashlsfd[1]fd[0]lsclose(fd[0])lsbash
  4. 我们再次 fork 来创建子进程less,它继承管道文件描述符。输入less需要从缓冲区中读取,因此我们使用fd[0].由于fd[0]不再是标准输入,我们需要重新映射它,所以我们close(0)dup(fd[0]).现在fd[0]将指向stdin,因此这里的输入是ls.less用于fd[0]读取并关闭写入端fd[1],因为less要防止其写入读取的数据再次被写出

答案3

在调用less 命令之前,您需要close在父级中 fd[1]否则命令将无限等待输入fork()less

相关内容