【问题标题】:Non-blocking pipes in UNIXUNIX 中的非阻塞管道
【发布时间】:2021-04-28 17:47:50
【问题描述】:

脚本

此 bash 脚本创建、写入和读取命名管道。但是,管道被阻塞了,所以cat 不会执行。

mkfifo pipe
echo Hello! > pipe // blocking
cat pipe           // deadlocked; unreachable

提案

将缓冲区附加到管道/fifo 文件,允许存储Hello!,直到它被cat 读取。这可以通过系统调用、stdbuf 等来实现。

类比

Go 编程语言具有通道,一种命​​名管道。然而,这些可以被缓冲,这样它们就可以非阻塞地存储消息(当然,直到缓冲区已满)。示例

...

func main() {
  channel := make(chan *byte, 1) // make a channel, with a buffer of 1
  
  write("Hello!", channel)   // write "Hello!" to the channel
  read(channel, fmt.Println) // read from the channel, and print the result
}

【问题讨论】:

  • Go 与 Unix 管道有什么关系?那是完全不同的事情。
  • @MarcoBonelli go 编程语言允许你创建 channels,它们基​​本上是你的 go 程序中函数的管道。在这里,我将 go 的通道缓冲功能与 Unix fifo 文件进行比较。
  • 嗯,也许您可​​以在“提案”部分添加一个实际问题。就目前而言,似乎没有提出任何实际问题,而且似乎需要进一步澄清,这就是为什么有人可能会否决它。
  • 没有“建议”部分。 Stack Overflow 是一个问答网站。我已编辑您的帖子以使其更清晰。
  • 也许我不清楚:我没有告诉您将帖子移动到任何地方。我告诉你在你的帖子中编辑## Proposal 之后的文本。我已经为你做到了。无需采取进一步行动。

标签: linux bash go system-calls


【解决方案1】:

我不完全确定这是否满足您的标准,但它似乎是一种有效的解决方法:

$ ( : < fifo & ); ( echo 'Hello!' > fifo & )
$ cat fifo
Hello! 

基本思想是打开一个读取器(它不消耗任何数据),以便echo 可以将数据写入管道。 (括号只是为了抑制作业控制输出,可以省略。)

使用起来可能更清楚:

$ sleep 1 < fifo &  echo 'Hello!' > fifo 
[1] 70954
$ sed 1q fifo
Hello!
[1]+  Done                    sleep 1 < fifo
$

但是有一点竞争条件。如果sleepecho 运行之前终止(不太可能),那么echo 将挂起。在后台运行echo 更安全。

【讨论】:

  • 第一行立即终止,导致第二行挂起并正常等待管道输入...有用,但需要修改。 +1
【解决方案2】:

管道阻塞,必须有一个接收端才能写入。我相信这是因为管道没有分配任何内存,并且无法在读取和写入之间存储信息。

你在这里几乎是正确的。命名管道阻塞,需要同时打开管道的读取器和写入器 才能工作。实际上在管道后面有一个由内核管理的缓冲区,但整个操作对用户程序是透明的。缓冲区仅在两个进程打开管道(一个用于写入,一个用于读取)之后创建。

所以在你的例子中,在这里:

mkfifo pipe
echo Hello! > pipe

echo 程序将等待open()O_WRONLY 的管道,因为open 系统调用阻塞在 FIFO 上,直到某个其他进程打开相同的 FIFO 进行读取。 FIFO 也不能以非阻塞方式打开,正如man 2 open 指定的那样:

ERRORS

[...]

       ENXIO  O_NONBLOCK | O_WRONLY is set, the named file is a FIFO,
              and no process has the FIFO open for reading.

您可以通过在 shell 中运行上述命令并注意到它无限期挂起来亲自看到这一点。如果您随后从管道打开另一个 shell 和 cat,则另一个 shell 上的 echo 将成功打开并写入管道,cat 将读取写入的内容。

在其他人打开 FIFO 进行读取之前,您将无法成功打开 FIFO 进行写入。这是设计使然。


为了给你的 Go 做一个类似的例子,这将是这样的:

go write()
go read()

注意:这只是一个类比,我在 Go 方面没有太多经验。

【讨论】:

  • 我知道管道的阻塞特性,并且我提供的 go 代码运行良好。我建议一种制作非阻塞管道的方法,该管道存储 一些信息,而不是等待接收者。我对 go 代码的观点是数据通道缓冲,即将信息存储在 data 通道中,因此它不需要阻塞。我在问是否有使用类似技术的 Unix 等价物。
  • @stackoverflowuser 我认为我的回答清楚地回答了这一点:你不能用 Unix 管道做到这一点。你想知道更多吗?如果是这样,请在问题中明确说明。如果您只是在寻找“另一种方法来做到这一点”,那么您可以使用文件、内存缓冲区和其他一千种东西来做到这一点。在这种情况下,这是一个非常广泛的问题。
  • 我会尝试研究解决方法...感谢您的帮助!
猜你喜欢
  • 2011-12-05
  • 2020-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-09
  • 2010-12-16
  • 2015-04-28
相关资源
最近更新 更多