2012-12-24 21 views
0

經過一些100年的組合後,我導致下面的代碼使用一個進程作爲服務器和管道來讀取來自客戶端的響應,我稱之爲'london'函數,它的管道'london_pipe'(我非常不確定要調用我的管道'倫敦'也是因爲命名衝突?)。 下面有兩個客戶端,每個客戶端都與自己的管道通信,並與服務器通信以讀取數據。我遵循相同的命名約定,使用讀取的函數名稱命名管道。所以,「柏林」是「berlin_pipe」等閱讀2個進程之間使用的命名管道(第3號)有什麼問題?

整體結構採用鋼管1對1的客戶端和1管服務器如下:

london ----writes madrid pipe--------> 
london <----reads london_pipe----<- | 
london ----writes berlin_pipe---> | | 
           |^| 
           | | | 
berlin <---reads berlin_pipe--<- | | 
berlin ----writes london_pipe-----| | 
           ^| 
            | | 
madrid ----writes london_pipe------ v 
madrid <----reads madrid_pipe------<- 

現在,當我運行代碼我面對一個非常奇怪的情況,我的客戶反覆閱讀空字符串,並且 - 因爲我構建了我的程序 - 他們響應向服務器發送虛驚一場!當我添加if子句時(請參閱代碼註釋),問題消失了!

任何人都可以解釋我的管道奇怪的行爲?或者代碼本身存在某種問題?

簡單地說,一些澄清: 代碼

while true; do 
....... 
done 4<"$pipename" 

適用於所有客戶,即使FD 4反覆使用,每次指向一個不同的管道。 現在代碼:

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

DIR=$(cd "$(dirname "$0")" && pwd) 

#the server 
function london(){ 
    local i message answer london_pipe berlin_pipe madrid_pipe 
    london_pipe=london_$RANDOM.$RANDOM.$RANDOM.$$ 
    madrid_pipe=madrid_$RANDOM.$RANDOM.$RANDOM.$$ 
    berlin_pipe=berlin_$RANDOM.$RANDOM.$RANDOM.$$ 
    cd $DIR 
    mkfifo $london_pipe 
    mkfifo $madrid_pipe 
    mkfifo $berlin_pipe 
    (madrid $madrid_pipe $london_pipe) & 
    (berlin $berlin_pipe $london_pipe) & 
    i=0 

    while true; do 
     if [[ i -gt 100 ]]; then 
     echo 'quit' > $madrid_pipe 
     echo 'quit' > $berlin_pipe 
     break 
     else 
     echo "loop #$i" 
     echo '=========' 
     message="London loop (#$i)" 

     #***send to Madrid***# 
     echo "$message" > $madrid_pipe 

     read -r answer <&3 
     echo 'London says:> '"$answer" 

     #***send to Berlin***# 
     echo "$message" > $berlin_pipe 

     read -r answer <&3 
     echo 'London says:> '"$answer" 
     ((i++)) 
     fi 
    done 3< $london_pipe 

    wait 
    cd "$DIR" 
    rm -rf $london_pipe 
    rm -rf $madrid_pipe 
    rm -rf $berlin_pipe 
} 

#a client 
function berlin(){ 
    local i message answer berlin_pipe london_pipe 
    berlin_pipe=$1 
    london_pipe=$2 
    cd $DIR 
    i=0 
    exec 3> $london_pipe 
    while true; do 

     read -r answer <&4 
     #***if deleted it reads empty strings!!!***# 
     if [[ ! $answer ]]; then 
     continue 
     fi 
     echo 'Berlin says:> '"$answer" 

     message="Greetings from Berlin!($i)" 
     echo "$message" >&3 
    ((i++)) 

     if [[ $answer = 'quit' ]]; then 
     break 
     fi 
    done 4< "$berlin_pipe" 
} 

#another client 
function madrid(){ 
    local i message answer madrid_pipe london_pipe 
    madrid_pipe=$1 
    london_pipe=$2 
    cd $DIR 
    i=0 
    exec 3> $london_pipe 
     while true; do 

     read -r answer <&4 
     #***if deleted it reads empty strings!!!***# 
     if [[ ! $answer ]]; then 
     continue 
     fi 
     echo 'Madrid says:> '"$answer" 

     message="Greetings from Madrid!($i)" 
     echo "$message" >&3 
     ((i++)) 

     if [[ $answer = 'quit' ]]; then 
     break 
     fi 
    done 4< "$madrid_pipe" 
} 

london 

的,如果代碼是該塊

 #***if deleted it reads empty strings!!!***# 
     if [[ ! $answer ]]; then 
     continue 
     fi 

有一些這方面的結果是:

loop #97 
========= 
Madrid says:> London loop (#97) 
London says:> Greetings from Madrid!(97) 
Berlin says:> London loop (#97) 
London says:> Greetings from Berlin!(97) 

沒有給兩個客戶端:

loop #91 
========= 
Madrid says:> London loop (#86) 
Madrid says:> London loop (#87) 
Madrid says:> London loop (#88) 
Madrid says:> London loop (#89) 
Madrid says:> London loop (#90) 
Madrid says:> 
Madrid says:> 
Madrid says:> 

很明顯,'馬德里'沒有讀過空字符串以及來自'倫敦'的具有循環編號86等的消息,這些消息已經被先前的空讀取所推動,並且現在無序。 有人可以解釋這一點嗎?

感謝

回答

1

問題是你如何寫管道在londonecho "$message" > $madrid_pipe 這打開和關閉管道的每個寫,不像你在其它端點做,你保持管道開放。這就產生了一個競爭條件:如果berlinmadrid發生在嘗試閱讀,同時在管道關閉,其讀取將不返回數據,請參見man 7 pipe

如果所有文件描述符指的是管道的寫端已經 已關閉,則嘗試從管道讀取(2)將看到 文件結束(read(2)將返回0)。

該解決方案很簡單,或者像你一樣處理這個問題,或者確保london保持管道打開。

+0

謝謝,我認爲有些事情現在已經闡明。最重要的是重定向字符關閉後的管道!我注意到的一件事是,服務器可以在進入其循環之前啓動帶有作業的客戶端,並且在進入之後立即開始捕捉客戶端的回聲而不丟失數據。我認爲,升級作業和進入客戶端循環之間的時間可以在服務器的管道中寫入災難結果,因爲循環不僅僅是打開了,但幸運的是數據在那裏!最後一件事是,'倫敦'怎麼打開它寫入的管道?無法想象它! – centurian

+0

就像柏林和馬德里那樣。只需使用'exec',然後寫入循環中的文件描述符。 – Jester