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

Sub*_*esh 7 pipe ipc

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

Den*_*ker 8

是的,用 pipe() 制作的管道有两个文件描述符。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 */
}
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Sub*_*esh 6

这是丹尼斯和我聊天对话 的摘要,经过简化,即使其他新手也可以使用它。执行时:ls | less

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