需要解释读取和命名管道的行为

Need to explain behavior of read and named pipe

我一直在为大学做项目,在里面我有一些小脚本可以对一个简单的数据库执行操作(比如 select 信息),一个服务器脚本,它接受来自各种实例的请求通过名为 server.pipe 的管道的客户端脚本和通过单个客户端的命名管道 returns 它们的结果(通常是文本文件的几行)。

对于多行响应,我尝试在客户端中使用 while 循环来继续从管道读取数据,我使用了这样的方法:

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

这有时会 return 完整结果,有时会 return 一部分,有时 none。当它没有 return 完整结果时,我认为 运行 脚本被管道阻塞,因为调试语句显示它没有执行它的最后几行。

我改用这个修复了它

tail <$return_pipe &

我知道 tail 会从管道读取,即使它关闭了,但我需要帮助解释导致第一个失败的条件,因为我现在正在写一份关于任务的报告。我读了很多书,有点明白了,但我需要有关细节的帮助。

如果有人想知道,服务器脚本是这样调用脚本的:

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

select 脚本的一部分 return 结果是这样的

echo "start_result"
cut -d' ' -f .//
echo "end_result"

如果有人能帮助我理解并解释它,我将不胜感激。这是我的第一个 post 所以我希望我也适当地格式化了它!谢谢

发生这种情况是因为 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。这样可以确保通信顺畅高效,不会丢失任何数据。