BASH:Fifo 麻烦 - 似乎对输入有选择性

BASH:Fifo 麻烦 - 似乎对输入有选择性

因此,我在 Debian 7 中的 BASH 服务上遇到了问题,我已经研究了很长一段时间,并且它的 fifo 随机开始出现问题,或者看起来是这样。它基于您的经典 fifo 使用示例,几个月来一直运行良好,但今天突然开始给我带来麻烦。似乎每当发生这样的事情时,它总是与我最初的结论完全不同,所以我会展示我所拥有的东西,也许有人可以向我指出我没有看到的明显部分。

正如我所说,我从命名管道读取/写入的代码是有点标准的。我制作了一个精简版本(150 行左右),我想我会展示它,但是,当然,它工作得很好,我不知道为什么。所以这是一个超级精简的版本供参考:

#--------------------------------Writer Script--------------------------------------#
#!/bin/bash

fifoIn=".../path/fifoIn"

#Read user input
IFS='' #Changed IFS so that spaces aren't trimmed from input
while true; do
    read -e line
    printf "%b\n" "$line" >&4
done 4>"$fifoIn"

exit 0

#--------------------------------Reader Script--------------------------------------#
#!/bin/bash

fifoIn=".../path/fifoIn"
LogFile=".../path/srvc.log"
[ -d ".../path" ] || mkdir -p ".../path"
[ -e "$fifoIn" ] || mkfifo "$fifoIn"

printf "%b\n" "Flushing input pipe" >> "$LogFile"
dd if="$fifoIn" iflag=nonblock of=/dev/null >/dev/null 2>&1

while true; do
    if read -t 0.1 -a str; then
        printf "\n%s\n" "<${str[*]}>"
        case "${str[0]}" in
            "foo")
                printf '%b\n' "You said foo..."
                ;;
            "bar")
                printf '%b\n' "You said bar..."
                ;;
            "")
                ;;
            *)
                printf "%b\n" "${str[*]}:"
                printf "%b\n" "Uhhuh..."
                ;;
        esac
    fi
done <"$fifoIn" >> "$LogFile" 2>&1 3>"$fifoIn"

echo因此,您采用“读取器脚本”并将其作为守护程序运行,然后通过ing 或ing与它对话printf或使用写入器脚本将消息发送到命名管道fifoIn.这从一开始就很有效,但今天却变得很奇怪。

由于某种原因,它开始对谁可以写入(或者至少看起来是谁可以写入)管道进行选择。我没有看到任何错误,但我尝试将文本发送到管道,但服务端不会发生任何事情。我设置了 cron 作业来写入管道,这些作业不会出现任何问题,而我echo从终端上却什么也得不到。甚至没有错误或权限被拒绝的消息。无论如何,cron 作业被设置为与我的终端相同的用户,所以我不认为这是一个权限问题。

似乎每次我删除 fifo 并重新启动服务时,我通常都可以收到一些终端输入的消息,但并非总是如此,在将 cron 发起的消息发送到服务后,这似乎会阻止或以其他方式停止工作。服务。我将无法再通过管道发送消息,但 cron 发起的消息将继续正常传输!

我做了一些谷歌搜索并发现了这个strace命令。我尝试做类似的事情strace printf '%b\n' "foo" >> .../path/fifoIn,得到一大堆我不太理解的诊断系统调用的东西,但看起来一切都有效,因为没有类似的东西,Hey! right here! something broke right here!!它以以下内容结束:

...
write(1, "foo\n", 4)
close(1)
...

我猜这是一件好事。现在有趣的是,消息通过了,守护进程按预期读取了它!我从那条线上删除了strace,再次没有骰子。

那么,所有比我更了解 io 操作和系统调用的人,当你有strace序言和没有序言时会发生什么不同?在没有关闭管道阅读的情况下,什么通常会粘住管道?您可能会发现任何其他线索,因为我不知所措。

更新

@Gilles,我认为你建议其他进程尝试读取同一管道并导致问题......我编写了一个新函数,该函数调用一些 mutt 实例,这些实例似乎fifoIn由于某种原因而有某种关联。我不太确定如何读取 的输出lsof,但在我执行该函数后它会读取此内容(因此会弄乱我的管道):

COMMAND     PID   TID        USER   FD      TYPE DEVICE SIZE/OFF     NODE NAME
mutt      13874           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      13874           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      13897           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      13897           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      13932           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      13932           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      13971           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      13971           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      14012           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      14012           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      14051           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      14051           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      14096           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      14096           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      14124           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
mutt      14124           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
srvc      14298           uname    0r     FIFO   8,17      0t0   393222 .../path/fifoIn
srvc      14298           uname    3w     FIFO   8,17      0t0   393222 .../path/fifoIn
lsof      15587           uname    1w     FIFO    0,8      0t0   176516 pipe
lsof      15587           uname    5w     FIFO    0,8      0t0   176524 pipe
lsof      15587           uname    6r     FIFO    0,8      0t0   176525 pipe
grep      15588           uname    0r     FIFO    0,8      0t0   176516 pipe
lsof      15589           uname    4r     FIFO    0,8      0t0   176524 pipe
lsof      15589           uname    7w     FIFO    0,8      0t0   176525 pipe

我猜我写错了 mutt 调用(最终在子 shell 中执行),但由于我对命令做错了什么,它们会锁定继承的 FD。我想说这就是答案,我将从那里得到答案!如果您发布“答案”,我很乐意选择它!

答案1

由于某种原因,它开始对谁可以写入(或者至少看起来是谁可以写入)管道进行选择。我没有看到任何错误,但我尝试将文本发送到管道,但服务端不会发生任何事情。

如果您的程序曾经可以运行,但同一个程序无法运行,请检查一下其环境是否已更改。

这些症状与管道上有多个读取器并且仅观察其中一个读取器是一致的。当多个进程从管道读取数据时,数据可以发送到任何进程。

您使用的是具有固定名称的命名管道。很可能您的程序中的某处有一个读取器部分的杂散实例。

您可以检查哪些进程打开了命名管道lsof

lsof .../path/fifoIn

如果管道上没有写入器,则可能会有读取器在open调用中被阻塞 - 打开命名管道会阻塞,直到写入器出现为止。lsof由于管道尚未打开,因此不会报告这些。我不知道如何找到open调用中被阻止的进程。您可以open通过打开它进行写入来使调用在所有进程中返回:

sleep 99999999 >.../path/fifoIn &
lsof .../path/fifoIn

请记住,打开的文件是由子进程继承的。如果您的程序在管道打开时在后台启动其他程序,则这些程序可能仍打开管道以供读取。您可能想关闭管道:

while … do
  subprocess_that_does_not_need_the_pipe </dev/null
done <.../path/fifoIn

或者

while … do
  subprocess_that_does_not_need_the_pipe 0<&3
done 3<&0 <.../path/fifoIn

相关内容