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 或printf
ing 或使用編寫器腳本將消息發送到命名管道與它對話,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