将命名管道与后台进程一起使用时的不同行为
Posted
技术标签:
【中文标题】将命名管道与后台进程一起使用时的不同行为【英文标题】:Varying behavior when using named pipe with background process 【发布时间】:2019-03-18 17:42:15 【问题描述】:我很难理解命名管道。我有以下脚本:
#!/bin/bash
function a_or_b
echo 'func begins'
while true; do
echo 'loop begins'
read -s -n 1; echo $?
case $REPLY in
'a') return 0 ;;
'b') return 1 ;;
esac
done
echo 'func ends'
mkfifo pipe
a_or_b <pipe &
现在我期待这个脚本做的是:
-
输入
a_or_b
,然后打印func begin
进入循环并因此打印loop begins
从 stdin 读取 EOF,因此从 pipe
(因为我没有写任何东西到 pipe
),因此将 1
打印为 @987654328 @
在任一 case-clause 上都不匹配,因此返回步骤 2
运行此脚本时,虽然我没有得到任何输出,但我的终端只是打印下一个提示。
如果我将 echo 重定向到 pipe
之前 调用 a_or_b
:
...
mkfifo pipe
echo 'x' > pipe
a_or_b <pipe &
...脚本不会停止运行,我可以继续在终端中输入字符(包括a
和'b')无效。所以我必须使用 ^C 结束脚本。
如果我将 echo 重定向到 pipe
之后 调用 a_or_b
:
...
mkfifo pipe
a_or_b <pipe &
echo 'x' > pipe
...我得到以下输出:
func begins
loop begins
0
loop begins
0
loop begins
1
loop begins
1
loop begins
1
这基本上是我所期望的行为,不会回应任何东西到pipe
。该函数开始,进入循环,从echo
中读取x
和\n
字符(对应于输出中的两个0
s),然后在无法读取任何字符时一直循环下去。如果我 回显 a
或 b
进入管道,则函数结束。
是什么导致了所有这些不同的行为?
【问题讨论】:
只是打开管道的读取端,而不是真正尝试从中读取,阻塞一个进程,直到另一端打开写入; 【参考方案1】:a_or_b <pipe &
在启动命令之前处理重定向。外壳阻止尝试打开 pipe
进行阅读。来自mkfifo(3) man page:
打开一个 FIFO 进行读取通常会阻塞,直到其他进程打开同一个 FIFO 进行写入,反之亦然。
在另一个进程打开 FIFO 进行写入之前,shell 无法继续。只有这样它才会完成重定向设置并实际调用a_or_b
。
echo 'x' > pipe
a_or_b <pipe &
不幸的是,这有相同但相反的问题。在另一个进程打开 FIFO 进行读取之前,shell 无法继续通过 > pipe
重定向。它永远不会到达echo
,也不会到达将从管道读取的第二行。
a_or_b <pipe &
echo 'x' > pipe
希望您现在可以了解此版本的工作原理。后台 shell 尝试从管道和块中读取。前台 shell,一个单独的进程,写入它。啊哈!两个进程打开了管道,一个用于读取,一个用于写入。他们现在都可以继续了。鸟儿歌唱,每个人都很开心。
【讨论】:
mkfifo
手册页指出“[a FIFO] 必须同时在两端打开,然后才能继续对其执行任何输入或输出操作。打开 FIFO 以正常读取块直到某个其他进程打开同一个 FIFO 进行写入,反之亦然。”为什么 first-function-call-then-echo-to-pipe 版本满足这个要求?为什么 first-echo-to-pipe-then-function-call 版本没有呢?我想我不明白您所说的“试图打开pipe
进行阅读的shell 块”是什么意思。
因为&
。工作的第一行在后台运行,因此它阻塞的事实不会阻止第二行运行。后台处理被屏蔽了,没关系。
如果您将&
移动到中间情况的第一行,那么它也可以工作。以上是关于将命名管道与后台进程一起使用时的不同行为的主要内容,如果未能解决你的问题,请参考以下文章