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


10

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

$ mkfifo f; exec <f

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

$ echo ls > f

现在window-1打印一个ls,然后外壳消失。为什么?

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

$ exec 3>f
$ echo ls >&3

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

答案必须与window-2中引用命名管道的文件描述符3 的存在有关!


1
exec <fbash不尝试从中读取f,它首先尝试将其打开。该open()不会返回,直到有一些做的过程中写入模式到管道(此时管将被实例化,并且外壳将从中读取输入)另一个打开。
斯特凡Chazelas

非常棒的一点,@StéphaneChazelas。这必须是为什么一旦exec 3>f运行后第一个shell给出提示的原因。(要点,您的意思是“ 模式”吗?)
Fixee,

1
是的,对不起。现在编辑只是5分钟截止日期前
斯特凡Chazelas

Answers:


12

它与文件描述符的关闭有关。

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

出现文件结束条件的原因是,命名管道的所有编写器(在本示例中只有一个)已关闭其管道的末端。

在第二个示例中,exec 3>f打开文件描述符3写入f,然后对其进行echo写入ls。现在是打开文件描述符的外壳,而不是echo命令。描述符保持打开状态,直到您这样做exec 3>&-。在接收端,从其标准输入流(连接到f)读取输入的shell 读取ls,运行ls,然后等待更多输入(因为该流仍处于打开状态)。

流保持开放状态,因为所有写入器(shell,via exec 3>fecho)都没有关闭管道的末端(exec 3>f仍然有效)。


我在echo上面写过,好像是外部命令一样。它最有可能内置在外壳中。尽管如此,效果还是一样的。


6

没什么大不了的:当管道中没有写入器时,它对读取器似乎是关闭的,即,读取时返回EOF,打开时阻塞。

在Linux手册页(pipe(7),另请参见fifo(7))上:

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

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


似乎有点类似于Java(和其他OO语言)中的引用计数!虽然有道理。
Fixee

2

阅读@Kusalananda和@ikkachu的两个答案后,我想我明白了。在window-1中,外壳程序正在等待打开管道的写端然后关闭它的操作。打开写端后,window-1中的shell将显示提示。关闭写端后,外壳将获得EOF并死亡。

在窗口-2侧,我们在我的问题描述的两种情况:在第一种情况下echo ls > f,没有文件描述符3,所以我们已经echo产卵,它stdinstdout这个样子的:

0 --> tty
1 --> f

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

在第二种情况下,我们exec 3>f在外壳中运行,导致外壳采用以下环境:

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本身对其进行引用。这是重要的区别。exec 3>&-如@Kusalananda所述,用结束描述符3 将关闭最后一个打开的引用,并使EOF变为window-1。


这是一个很好的例子,说明除非有充分的设计理由要修改前三个文件描述符,否则为什么不应该保留前三个文件描述符。当使用描述符(1)最终成为另一个shell的输入描述符(0)时,您不仅关闭了管道(以及您对该特定数据流所做的工作),还关闭了第二个输入的输入导致它终止的外壳。很好,但前提是您是故意这样做的。使用编号更高的文件描述符可避免此类副作用,因为没有人期望它们处于任何特定状态甚至被定义。

老实说,我不确定在该评论中要说的是什么,我将其删除。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.