需要说明读取和命名管道的行为

时间:2018-11-24 16:52:16

标签: bash concurrency pipe fifo

我一直在从事大学项目,其中有一些小的脚本,它们在简单的数据库上执行操作(例如选择信息),服务器脚本通过管道从客户端脚本的各种实例获取请求命名为server.pipe并通过单个客户端的命名管道返回其结果(通常是文本文件的几行)。

对于多行响应,我尝试在客户端中使用while循环以继续从管道中读取内容,我使用了类似的方法:

read response < $id.pipe
while [ $response != "end_result" ]; do
    echo $response
    read response < $id.pipe

这有时会返回完整的结果,有时会返回部分结果,而有时则不返回全部结果。当它没有返回完整结果时,我认为正在运行的脚本被管道阻塞,因为调试语句向我表明它没有执行其最后几行。

我用这个代替它

tail <$return_pipe &

我知道即使关闭了管道,尾部也会从管道中读取信息,但是我需要帮助解释导致第一个管道出现故障的情况,因为我现在正在编写有关分配的报告。我已经读了很多书,但我需要一些帮助。

如果有人想知道,服务器脚本正在像这样调用脚本:

return_pipe=${ar[4]}.pipe
./select.sh ${ar[1]} ${ar[2]} ${ar[3]} >$return_pipe &

选择脚本返回结果的部分是这个

echo "start_result"
cut -d' ' -f$3 ./$1/$2
echo "end_result"

如果有人能帮助我理解并解释这一点,我们将不胜感激。这是我的第一篇文章,所以我希望我也对它进行了适当的格式化!谢谢

1 个答案:

答案 0 :(得分:0)

发生这种情况是因为fifo不是面向数据包的。这是一条小溪。

这意味着可能在内存中串联多个写入,并且在不读取所有管道缓冲区的情况下关闭管道会丢弃其余部分。 这是否会发生是一个种族条件。这是有效的事件顺序:

  • 服务器为msg1打开fifo
  • 客户端为msg1打开fifo
  • 服务器写入msg1
  • 服务器关闭fifo
  • 客户端读取msg1
  • 客户关闭fifo
  • 对msg2以相同的顺序重复以上操作

下面是一个带有睡眠的代码示例,以确保顺序良好:

rm fifo; mkfifo fifo
{
  echo "hello" > fifo
  sleep 1
  echo "world" > fifo
} &

for i in 1 2
do
  echo "Reading..."
  { read var; } < fifo
  sleep 1
  echo "Read: $var"
done

这将输出您期望的结果:

Reading...
Read: hello
Reading...
Read: world

这是失败的事件顺序:

  • 服务器为msg1打开fifo
  • 客户端为msg1打开fifo
  • 服务器写入msg1并关闭fifo
  • 服务器为msg2打开fifo
  • 服务器写入msg2并关闭fifo
  • 客户端读取msg1
  • 当msg2仍未读取时,客户端关闭fifo。 msg2现在丢失了。

需要一些睡眠才能做到这一点:

rm fifo; mkfifo fifo
{
  echo "hello" > fifo
  echo "world" > fifo
} &

for i in 1 2
do
  echo "Reading..."
  { sleep 1; read var; } < fifo
  echo "Read: $var"
done

现在它将打印:

Reading...
Read: hello
Reading...

并挂起,因为world丢失了。

您应该改为从服务器的两个客户端打开一次fifo,并继续写入同一打开的FD。这样就可以确保流畅高效的通信,而不会丢失任何数据。