2012-12-21 25 views
2

我做了2個並行運行的進程與命名管道進行通信。 我注意到一個奇怪的行爲:每一個寫都應該跟着一個閱讀,反之亦然! 如果我們違反了規則,程序就會掛起,如果我們用ctrl + C終止它,那麼孩子仍然掛起,這意味着它不能再重新讀取。2個進程之間使用的命名管道有什麼問題?

我的例子:

#!/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 

在那裏我已經插入了「寫入到寫」或「讀閱讀」信息點有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閱讀!有沒有辦法做到這一點?

回答

2

管道有數量有限的數據,他們可以容納。如果直到寫滿爲止,所有將來的寫入都會阻塞,直到數據讀出。同樣,如果管道中沒有數據要讀取,則讀取塊。

最重要的是,你需要管道兩側的人,才能做事。所以,當你寫完第二條消息後,你會遇到讀者看管道的情況。所以,就它而言,沒有數據。然後,您的主要流程結束,並將孩子留在外面。

要解決這個問題,請不要關閉閱讀器端。使用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 
+0

是啊,這是正確的答案,通過完整的機會,我發現這個(http://stackoverflow.com/questions/4290684/using-named-pipes-with-bash-problem-with-data -loss?rq = 1),實際上支持你的答案!謝謝!當然,一根管子的「數據量」是多少? 2^20個字節還是什麼? – centurian

+0

...和最後一個問題:是否可以接受而不是使用成對讀寫的循環?關於代碼效率(我懷疑我們一次又一次地重新打開管道,這是不好的!) – centurian

+1

@centurian命名管道一直存在,直到你刪除它,所以使用while循環非常有效。要檢查管道的大小,請在bash中使用'ulimit -a'來獲取當前設置的管道大小。但是,正如我所提到的,如果管道已滿,作者將會阻止,並且當有額外的空間時,它將繼續寫入而不會丟失數據。 –