两个进程之间使用的命名管道有啥问题?

Posted

技术标签:

【中文标题】两个进程之间使用的命名管道有啥问题?【英文标题】:What's wrong with named pipes used between 2 processes?两个进程之间使用的命名管道有什么问题? 【发布时间】:2012-12-21 14:19:24 【问题描述】:

我让 2 个进程并行运行,它们与命名管道通信。 我注意到一个奇怪的行为:每次写入都应该跟着读取,反之亦然! 如果我们违反规则,程序会挂起,如果我们用 ctrl+C 终止它,孩子仍然会挂起,这意味着它无法再重新读取。

我的例子:

#!/bin/bash
shopt -u failglob
shopt -s extglob nullglob dotglob

function london ()
   local message answer fifo id return exitcode
   fifo=fifo_$RANDOM.$RANDOM.$RANDOM.$$
   mkfifo $fifo
   #trap 'rm -rf "$fifo"' EXIT
   ( berlin $fifo ) &
   id=$!
   echo "parent id: $$, child id: $id"
   message='Greetings from London!(1)'
   echo "$message" > $fifo
   echo "1. parent sent it's 1st message"
   #*****write-to-write error!*****#
   message='Greetings from London!(2)'
   #read -r answer < $fifo
   echo "$message" > $fifo
   echo "2. parent sent it's 2nd message"
   wait
   rm -rf $fifo


function berlin ()
   local message answer fifo
   fifo=$1
   read -r answer < $fifo
   echo 'Berlin says:> '"$answer"
   #*****read-to-read error!*****#
   #echo 'next' > $fifo
   read -r answer < $fifo
   echo 'Berlin says:> '"$answer"


london

在我插入“write-to-write”或“read-to-read”消息的点下,有 2 条注释行解决了这个问题,让我觉得上面的规则神秘地成立了!!! 有什么想法吗?

这是输出:

parent id: 4921, child id: 4923
1. parent sent it's 1st message
2. parent sent it's 2nd message
Berlin says:> Greetings from London!(1)

谢谢!

我想现在一切都清楚了,浓缩成一句话:“保持管道畅通无阻”

现在假设,我想为循环中的“一些”命令添加第二个输入文件描述符;我怎样才能做到这一点? 这是我的新柏林函数:

function berlin ()
   local message answer fifo
   fifo=$1
   while true; do
      read -r answer
      echo 'Berlin says:> '"$answer"
      #this fails!
      read -r -p "reading from fd0: " <&0
      if [[ $answer = 'quit' ]]; then
         break
      fi
   done < "$fifo" 3>"$fifo"

正如我们所看到的,我们使用文件描述符 3 作为管道,但是当我们尝试从 fd 0 读取时,我们实际上是从 fd 3 读取的!有没有办法做到这一点?

【问题讨论】:

【参考方案1】:

管道可以容纳的数据量有限。如果您写入它们直到它们已满,则所有未来的写入都将阻塞,直到数据被读出。同样,如果管道中没有要读取的数据,则读取块。

最重要的是,在你做事之前,你需要管道两边的人。因此,您遇到了一种情况,即在您编写第二条消息后,读者会查看管道。因此,就它而言,没有数据。然后,您的主要过程完成,让孩子闲逛。

要解决此问题,请不要关闭阅读器端。使用 while 循环保持该端打开。像这样:

#!/bin/bash
shopt -u failglob
shopt -s extglob nullglob dotglob

function london ()
   local message answer fifo id return exitcode
   fifo=fifo_$RANDOM.$RANDOM.$RANDOM.$$
   mkfifo $fifo
   #trap 'rm -rf "$fifo"' EXIT
   ( berlin $fifo ) & 
   id=$!
   echo "parent id: $$, child id: $id"
   message='Greetings from London!(1)'
   echo "$message" > $fifo
   echo "1. parent sent it's 1st message"
   #*****write-to-write error!*****#
   message='Greetings from London!(2)'
   #read -r answer < $fifo
   echo "$message" > $fifo
   echo "2. parent sent it's 2nd message"
   wait
   rm -rf $fifo


function berlin ()
   local message answer fifo
   fifo=$1
   while read -r answer
   do  
       echo 'Berlin says:> '"$answer"
   done < $fifo


london

【讨论】:

是的,这是正确的答案,我完全有机会发现这个(***.com/questions/4290684/…)实际上支持你的答案!谢谢!当然,管道包含“多少”数据? 2^20 字节还是什么? ...还有最后一个问题:使用读写对代替循环是否可以接受?关于代码效率(我怀疑我们一次又一次地重新打开管道,这很糟糕!) @centurian 命名管道会一直存在,直到您将其删除,因此使用 while 循环非常有效。要检查管道的大小,请在 bash 中使用 ulimit -a 来获取当前设置的管道大小。但是,正如我所提到的,如果管道已满,写入器将阻塞,当有额外空间时,它将继续写入而不会丢失数据。

以上是关于两个进程之间使用的命名管道有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

使用命名管道在两个进程之间发送字符串

管道和命名管道

进程之间的通信-命名管道通信

命名管道

简述Linux进程间通信之命名管道FIFO

Linux进程间通信 -- 使用命名管道