BASH/SH 中具有一个 READER 和多个 WRITER 的 FIFO

Posted

技术标签:

【中文标题】BASH/SH 中具有一个 READER 和多个 WRITER 的 FIFO【英文标题】:FIFO with one READER and multiple WRITER in BASH/SH 【发布时间】:2020-10-22 15:56:36 【问题描述】:

我有一个命名管道(FIFO)说:'MY_FIFO'

一个进程从多个进程中读取fifo来获取指令。

(1) 阅读器 bash 脚本:

while read LINE < 'MY_FIFO'
do
 # process line
done

(许多)编写器 bash 脚本:

while [ 1 ]
do
  INSTRUCTION = # get an instruction
  echo $INSTRUCTION > 'MY_FIFO'
done

当我们有 1 个写入者或写入者不同时写入时,这可以很好地将指令传递给阅读器。但是,如果我们同时有多个写入器写入,读取器会从最先写入 FIFO 的人那里获得第一条消息,然后挂起等待输入而不是获取下一条指令。

导致我无法让多个写入器写入同一个 fifo 的幕后发生了什么? 他们是否会覆盖彼此的指令?

我有没有办法在写入 fifo 之前测试读取器是否准备好从 fifo 读取?

【问题讨论】:

能否创建一个真正的minimal reproducible example? 不是真正的 bash 或 sh 问题;这是 FIFO 语义的问题,您也可以用 C 编写相同的代码。 (注意,为了确定什么构成“相同代码”,当您退出重定向适用的代码时,文件句柄被显式关闭,并打开一个全新的文件句柄下次您输入该代码块时)。 ...无论如何,解决此问题的一种简单方法是 进行太多更改,即拥有一个flock 风格的独占锁,在任何编写器打开之前都需要持有该锁文件。 @willer2k, ...btw,顺便说一句,请注意,POSIX 规范为修改 shell 或系统实用程序行为的变量指定全大写变量名,而变量名至少有一个小写字符保留“供应用程序使用”,这意味着它们保证不会覆盖修改外壳行为的内容。请参阅pubs.opengroup.org/onlinepubs/9699919799/basedefs/…,请记住环境变量和常规 shell 变量共享一个命名空间(设置 shell 变量将覆盖任何同名环境变量)。 【参考方案1】:

一个问题是,当作者写完它的消息时,它会关闭管道,然后你的阅读器将退出那个循环。当多个进程同时写入时,您还会在管道中收到乱码消息。

你可以试试这个。它在写入时使用flock 锁定管道:

阅读器

#!/bin/bash

while [[ 1 ]]
do
    IFS= read -r LINE < MY_FIFO
    echo reader got "$LINE"
done

writers - 注意这里先打开管道,然后等待锁成功,最后写入消息:

#!/bin/bash

# getting instructions from stdin for demo purposes
while IFS= read -r INSTRUCTION
do
    echo Sending "$INSTRUCTION"

    # advisory lock of the pipe when writing
    flock MY_FIFO echo "$INSTRUCTION" > MY_FIFO
done

可选的writers - 这会创建一个锁文件并且在实际持有锁之前不会打开fifo。

#!/bin/bash

while IFS= read -r INSTRUCTION
do
    echo Sending "$INSTRUCTION"
    
         flock 9
         echo "$INSTRUCTION" > MY_FIFO
     9> MY_FIFO.lck
done

【讨论】:

这是为输出打开 MY_FIFO 以输出 before flock 运行,因为重定向发生在目标命令的 exec 之前。 考虑改为exec lock_fd&gt;MY_FIFO.lck; flock -x "$lock_fd"; echo "$INSTRUCTION" &gt;MY_FIFO; exec lock_fd&gt;&amp;-——这样编写者在持有锁之前根本不会触及FIFO本身。 (如果需要兼容 4.1 之前的 bash,可以手动分配一个 FD)。 @CharlesDuffy 我还没有修复它,但我在想 - 如果它先打开文件有没有关系,因为它在锁定之前不会写入? 好问题。老实说,我在这里的目标是最大的可预测性 - 只有一个写入器一次打开​​文件意味着读取器将明确阻止打开操作,直到特定写入器打开文件,并且确保在写入端没有任何内容时写入器之间会有一段时间,因此读取器可以获得 EOF;因此,同步更容易推理。 @CharlesDuffy 我同意。我会添加一个选项。

以上是关于BASH/SH 中具有一个 READER 和多个 WRITER 的 FIFO的主要内容,如果未能解决你的问题,请参考以下文章

linux bash 入门

执行脚本source 和 . 和sh 的区别是什么

具有多个缓冲区的片段着色器颜色错误

reader.ReadToEnd 和 Stream.Read 之间的区别

使用 bash.sh 拒绝运行 cron 的权限

Java:当具有受保护的构造函数时,如何从 java.io 为 Reader 类创建新的类对象