据我了解,管道的一端既可以读写 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
:
- bash shell 是具有
fd[0]
、fd[1]
、 和 的父进程fd[2]
。 pipe()
由父级调用,它创建一个管道,该管道只不过是一个容纳数据的缓冲区,并且它的每一端都有一个文件描述符,一个用于读取,一个用于写入。- 子进程继承所有打开的fd; 0、1 和 2,以及管道的两个。因此,和
ls
都有自己的缓冲区副本,但它们都指向来自 的相同管道 fds 。在第一个子进程内执行,现在我们需要将输出重新映射到管道 ( ) 的写入端,然后映射到控制台。因此并将进行重新映射。只影响子进程,不影响父进程。现在, 的输出将定向到子级管道的写入端。仍然是读取端,我们不希望数据自己读取,所以我们仍然需要用 关闭该端。同样,这仅关闭 中的文件描述符,而不是.fd[0]
fd[1]
bash
ls
fd[1]
close(1)
dup(fd[1])
close(1)
ls
bash
ls
fd[1]
fd[0]
ls
close(fd[0])
ls
bash
- 我们再次 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