沒有正確使用FIFO的程序就會發生微妙的問題。若是咱們沒有任何進程打開某FIFO來寫,那麼打開該FIFO來讀的進程就會阻塞。
若是父子進程都打開同一個FIFO來讀,然而沒有任何其餘進程已打開該文件來寫,因而父子進程都會阻塞,這種現象叫死鎖(deadlock)
因此打開文件和打開FIFO仍是不同的。shell
內核爲管道和FIFO維護着一個訪問計數器,他的值是訪問同一個管道或FIFO的打開着的描述符的個數。
有了訪問計數器後,客戶或者服務器就能成功地調用unlink,儘管該函數從文件系統中刪除了所指定的路徑名,先前已經打開着的描述符卻不受影響。
然而,對其餘形式的IPC來講,如System V消息隊列,這樣的計數器是不存在的。所以要是服務器在向某個消息隊列寫入了本身的最終消息後刪除了改隊列,那麼當客戶端嘗試去讀取這個最終消息時,該隊列可能已經消失了。服務器
用shell命令 echo 把數據寫入到管道test.pip中,
用另一個進程去讀取管道中的數據(cat),這個兩個命令之間能夠間隔任意長時間。表面上,可能會使咱們認爲即便沒有進程打開着的FIFO,數據也會以某種形式保存在該FIFO中。其實不是這樣的。
真正的規則:當對一個管道或者FIFO的最終close發生時,該管道中的任何殘餘數據都會被丟棄。當echo數據到test.pip時,該FIFO的open調用會一直阻塞到咱們在之後的某個時刻執行cat命令爲止。函數
mkfifo test.pip #建立一個 FIFO echo '123' > test.pip
第二個shell線程
echo '456' > test.pip
第三個shellcode
echo '789' > test.pip
這幾個shell都會阻塞,直到以下shell隊列
cat test.pip #輸出以下: 789 456 123 #而後上面三個shell阻塞取消,進入正常輸入模式
因此說,管道或者FIFO不能保存數據,若是要寫入,必然是有進程在讀,若是要讀,必然是有進程在寫。否則就會阻塞,直到阻塞取消,才能讀寫。雖然管道的打開關閉讀寫的函數和普通文件同樣,可是結果是不一致的。好比,以寫的方式打開一個文件,而後關閉文件,文件內容就清空了。若是是管道或者FIFO,以寫的方式打開的進程們都會被阻塞,知道有進程來讀取,這時這些阻塞的open調用纔會進入正常模式,依次往FIFO中寫入數據,先進先出,被讀進程取出。
固然以上都是管道的默認行爲,能夠經過設置非阻塞標誌來影響一個管道或FIFO的行爲進程
當前操做 | 管道或FIFO的現有打開操做 | 阻塞(默認) | O_NONBLOCK設置 |
---|---|---|---|
open FIFO只讀 | FIFO打開來寫 | 返回成功 | 返回成功 |
同上 | FIFO不是打開來寫 | 阻塞到FIFO打開來寫爲止 | 返回成功 |
open FIFO只寫 | FIFO打開來讀 | 返回成功 | 返回成功 |
同上 | FIFO不是打開來讀 | 阻塞到FIFO打開來讀爲止 | 返回ENXIO錯誤 |
從空管道或空FIFO read | 管道或者FIFO打開來寫 | 阻塞到管道或FIFO中有數據或者管道或FIFO再也不爲寫打開着爲止 | 返回EAGAIN錯誤 |
同上 | 管道或者FIFO不是打開來寫 | read返回0(文件結束符) | read返回0(文件結束符) |
往管道或FIFO中write | 管道或FIFO打開來讀 | (通常正常,若干規則) | (通常正常,若干規則) |
同上 | 管道或FIFO不是打開來讀 | 給線程產生SIGPIPE | 給線程產生SIGPIPE |