据我了解,管道的一端既可以读写 fd,另一端也可以读写 fd。这就是为什么当我们使用 using 写入时fd[1]
,我们关闭了读取端,例如fd[0]
管道同一侧的读取端,而当我们从第二端读取 using 时,fd[0]
我们关闭了fd[1]
该端的读取端。我对么?
是的,用 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 之后,它实际上也关闭了另一端)。
这是丹尼斯和我聊天对话 的摘要,经过简化,即使其他新手也可以使用它。执行时:ls | less
fd[0]
,fd[1]
和fd[2]
。pipe()
由父级调用,它创建一个管道,它只是一个用于容纳数据的缓冲区,并且在每一端都有一个文件描述符,一个用于读取,一个用于写入。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
.less
继承管道文件描述符。输入到less
需要从缓冲区读取,因此我们使用fd[0]
. 由于fd[0]
不再是标准输入,我们需要重新映射它,所以我们close(0)
和dup(fd[0])
. 现在fd[0]
将指向标准输入,所以这里的输入是ls
. less
用途fd[0]
用于读取和关闭写端fd[1]
作为less
要防止其写入数据的读取再次写出