Shell-Script

BASH:Fifo 問題 - 似乎對輸入有選擇性

  • May 29, 2015

所以我在 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"

因此,您使用“閱讀器腳本”並將其作為守護程序執行,然後通過echoing 或printfing 或使用編寫器腳本將消息發送到命名管道與它對話,fifoIn. 這從一開始就很有效,但今天變得很奇怪。

出於某種原因,它開始對誰可以寫(或至少似乎是誰可以寫)到管道變得挑剔。我沒有看到任何錯誤,但我會嘗試將文本發送到管道並且服務端不會發生任何事情。我已經設置了 cron 作業來寫入管道,而這些作業不會有任何問題,而我echo從終端執行的操作將一無所獲。甚至沒有錯誤或權限被拒絕消息。無論如何,cron 作業都設置為與我的終端相同的使用者,所以我認為這不是權限問題。

似乎每次我刪除先進先出並重新啟動服務時,我通常會收到一些終端輸入的消息,但並非總是如此,在將 cron 發起的消息發送到服務。我將不再能夠通過管道發送消息,但 cron 發起的消息將繼續通過就好了!

我做了一些Google搜尋,發現了這個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,我認為您正在建議其他程序嘗試讀取同一管道並導致問題…我編寫了一個新函式,該函式呼叫了一些似乎與fifoIn某種原因相關的 mutt 實例。我不太確定如何讀取 的輸出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 上。我會說這就是答案,我會從那裡拿走它!如果您發布“答案”,我很樂意選擇它!

出於某種原因,它開始對誰可以寫(或至少似乎是誰可以寫)到管道變得挑剔。我沒有看到任何錯誤,但我會嘗試將文本發送到管道並且服務端不會發生任何事情。

如果您的程序過去可以工作,而同一個程序無法正常工作,請檢查一下它的環境是否可能發生了變化。

這些症狀與管道上有多個閱讀器並僅觀察其中一個閱讀器是一致的。當多個程序從管道中讀取數據時,數據可以進入任何程序。

您正在使用具有固定名稱的命名管道。很可能您的程序的閱讀器部分在某處有一個雜散的實例。

您可以檢查哪些程序打開了命名管道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

引用自:https://unix.stackexchange.com/questions/205637