命名管道、文件描述符和 EOF

命名管道、文件描述符和 EOF

两个窗口,同一用户,带有 bash 提示符。在 window-1 中输入:

$ mkfifo f; exec <f

因此 bash 现在尝试从文件描述符 0 读取,该描述符映射到命名管道f。在窗口 2 中输入:

$ echo ls > f

现在 window-1 打印一个 ls 然后 shell 就死掉了。为什么?

下一个实验:使用 再次打开 window-1 exec <f。在窗口 2 中输入:

$ exec 3>f
$ echo ls >&3

在上面的第一行之后,window-1 被唤醒并打印一条提示。为什么?在上面的第二行之后,window-1 打印ls输出并且 shell 保持活动状态。为什么?事实上,现在在window-2中,echo ls > f并没有关闭window-1 shell。

答案一定与存在window-2 中的文件描述符 3 引用命名管道?!

答案1

这与关闭文件描述符的。

在第一个示例中,echo写入其标准输出流,外壳程序打开该流以将其与 进行连接f,并且当它终止时,其描述符将被关闭(由外壳程序)。在接收端,从其标准输入流(连接到f)读取输入的 shell会读取ls、运行ls,然后由于其标准输入上的文件结束条件而终止。

发生文件结束条件是因为命名管道的所有编写者(本示例中只有一个)都已关闭其管道末尾。

在第二个示例中,exec 3>f打开文件描述符 3 以写入f,然后echo写入ls它。现在是 shell 打开了文件描述符,而不是命令echo。描述符将保持打开状态,直到您执行此操作exec 3>&-。在接收端,从其标准输入流(连接到f)读取输入的 shell读取ls、运行ls,然后等待更多输入(因为流仍然打开)。

流保持打开状态,因为它的所有写入者(shell、viaexec 3>fecho)都已不是关闭了管道的末端(exec 3>f仍然有效)。


我在上面写的就echo好像它是一个外部命令一样。它很可能内置于外壳中。尽管如此,效果是一样的。

答案2

没有太多内容:当管道没有写入者时,它看起来对读取器关闭,即读取时返回 EOF,打开时阻塞。

从 Linux 手册页 (pipe(7),但另请参阅fifo(7)):

如果引用管道写入端的所有文件描述符都已关闭,则尝试从read(2)管道读取文件将看到文件结尾(read(2)将返回 0)。

关闭写入端是在 the 末尾隐式发生的事情echo ls >f,正如您所说,在另一种情况下,文件描述符保持打开状态。

答案3

阅读了@Kusalananda 和@ikkachu 的两个答案后,我想我明白了。在 window-1 中,shell 正在等待某些东西打开管道的写入端然后将其关闭。一旦打开写入端,window-1 中的 shell 就会打印一条提示。一旦写端关闭,shell 就会收到 EOF 并终止。

在 window-2 方面,我们有我的问题中描述的两种情况:在第一种情况下echo ls > f,没有文件描述符 3,所以我们echo产生了,它的stdinstdout看起来像这样:

0 --> tty
1 --> f

然后echo终止并且 shell 关闭两个描述符。由于文件描述符 1 已关闭并引用f,因此 的写入结束f已关闭,这会导致 window-1 出现 EOF。

在第二种情况下,我们exec 3>f在 shell 中运行,使 shell 采用此环境:

bash:
0 --> tty
1 --> tty
2 --> tty
3 --> f

现在我们运行echo ls >& 3,shell 分配文件描述符如下echo

echo:
0 --> tty
1 --> f     # because 3 points to f
2 --> tty

然后 shell 关闭上面的三个描述符,包括f,但f仍然有 shell 本身对它的引用。这是重要的区别。关闭描述符 3exec 3>&-将关闭最后一个打开的引用并导致 EOF 到 window-1,如 @Kusalananda 指出的。

相关内容