【问题标题】:Concurrent writes to mkfifo seem to get lost对 mkfifo 的并发写入似乎丢失了
【发布时间】:2019-04-13 20:27:30
【问题描述】:

我正在编写一个脚本来设置机器环境。我有能力在机器上放置一个启动脚本,所以我尝试在我的“工作站”机器上使用 unix 中的命名管道mkfifo,当这些新机器上线时,它将监听来自这些新机器的写入。

问题是,我无法控制这些机器何时上线。总共有 20 多台机器同时启动,我试图模拟命名管道在有机会读取之前被多次写入的效果,并产生了惊人的结果....

我写了两个小测试脚本:

mkfifogen.sh

#!/bin/bash
mkfifo h
testing=""
sleep 10
for((i=0;i<5;i++));do
    echo $I
    while read line;do
       testing+="$line"
    done < h
done

echo "$testing"
rm -f h

mkfifowrite.sh

#!/bin/bash
for((i=0;i<5;i++));do
    echo "this is a test $i" > h
done

当我运行 mkfifogen.sh 后跟 mkfifowrite.sh 时,输出如下

0
1
2

然后mkfifowrite.sh脚本退出,mkfifogen.sh脚本挂起

然后我必须再运行 mkfifowrite.sh 脚本 3 次,每次都会导致 mkfifogen.sh 脚本上的输出再增加一次。最后一次运行会导致 testing 的值输出 4 次,所以我的 mkfifowrite.sh 脚本运行 4 次和 mkfifogen.sh 脚本运行一次后的输出如下所示:

0
1
2
3
4
this is a test 0this is a test 1this is a test 2this is a test 3this is a test 4
this is a test 0this is a test 1this is a test 2this is a test 3this is a test 4
this is a test 0this is a test 1this is a test 2this is a test 3this is a test 4
this is a test 0this is a test 1this is a test 2this is a test 3this is a test 4

在运行 mkfifogen.sh 和 mkfifowrite.sh 后,我期望看到的是:

0
1
2
3
4
this is a test 0this is a test 1this is a test 2this is a test 3this is a test 4

我在网上看了很多,对此有一些神秘的答案,但我似乎无法从中理解。我觉得这个链接可能是我的答案,但我不确定https://unix.stackexchange.com/questions/68146/what-are-guarantees-for-concurrent-writes-into-a-named-pipe

TL;DR:

如何确保对命名管道的所有写入(无论它们执行的速度如何)都由读取独立处理?

【问题讨论】:

  • 投反对票,你有理由吗?如果是这样,请评论我如何改进我的问题...
  • 这有点棘手。首先,您是有意在每次迭代时打开和关闭mkfifowrite.sh 中的管道,还是希望mkfifowrite.sh 的单次运行对应mkfifogen.shfor 循环的单次迭代?其次,根据我使用的bash 的版本,以及我是在macOS 还是Linux 下运行它,我得到了不同的结果。您使用的是哪个版本的bash
  • 是的,for 循环的每次迭代都会模拟对管道的不同写入,就像一台机器上线并中继其状态一样。我在 RHEL 上运行它。 @chepner
  • 基本上,我想了解在读取尝试读取第一个之前,当写入在管道上堆积时会发生什么。
  • 请注意,您不是在测试并发写入;你有一个读者和一个作家,而那个作家只是在进行一系列顺序写入。如果您尝试在 parallel 中多次运行mkfifowrite.sh,就会出现真正的问题。

标签: linux bash unix pipe named-pipes


【解决方案1】:

阅读器读取到 EOF,然后重复,直到收到 5 个 EOF。

编写器打开和关闭管道 5 次。每次编写器关闭管道时,都会存在潜在的 EOF 条件。直到读者尝试阅读它(在阅读了之前写的所有内容之后),它才会成为实际的 EOF。

在这个潜在的 EOF 阶段,有一场竞赛。写入器正在递增i 并准备打开管道再次写入,而读取器正在完成read 命令并处理结果,之后它将尝试再次读取。

如果写入器在读取器尝试读取之前执行打开写入,则潜在的 EOF 条件将消失。如果读取器在写入器不再连接到管道时尝试读取,则会发生 EOF。

如您所见,当管道中有多个写入器(无论是顺序的还是并行的)时,EOF 会很痛苦。您不能期望它们可靠地出现在来自不同作者的消息之间。我的出路是设计协议,使阅读器不需要 EOF 来识别消息的结尾,然后让阅读器以读写模式打开管道,这样 总是 em> 至少是一个现有的作家,EOF 永远不会发生。

将消息保持在PIPE_BUF 的大小以防止它们被拆分是一个单独的问题,您也必须注意这一点。

【讨论】:

  • 多个写入器是顺序的。在前一个完成之前,一个不会开始。那里没有竞争条件。
  • @chepner 我没有说两位作家之间有比赛。我描述了(非常详细!)作者和读者之间的竞赛。
猜你喜欢
  • 2016-09-28
  • 1970-01-01
  • 2018-07-13
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
  • 2018-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多