Linux進程間通信之命名管道(FIFO)

上一篇講的是管道(pipe),也提到過管道最大的缺點是沒有名字,因此只能用於具有親子關係的進程之間。
何爲沒有名字?使用過Linux系統的人,一定都聽說過一句話:在Linux系統中一切皆文件。
對於這裏所說的文件,系統中都會有個名字與之對應。這樣我們就可以通過名字對文件進行讀寫等操作。
但是管道在系統中沒有這樣的名字,因此無法通過IO函數對其進行訪問,所以其只能在父子進程間使用。這也是管道最大的缺陷。

本節講述的命名管道(FIFO)將不存在無名的問題。

1.創建FIFO

#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

該函數的第一個參數是一個普通的路徑名,也就是創建後FIFO的名字。第二個參數與打開普通文件的open()函數中的mode參數相同。 

如果mkfifo的第一個參數是一個已經存在的路徑名時,會返回EEXIST錯誤,所以一般典型的調用代碼首先會檢查是否返回該錯誤。

如果確實返回該錯誤,那麼只要調用打開FIFO的函數就可以了。一般文件的I/O函數都可以用於FIFO,如closereadwrite等等。

下面來創建一個FIFO

  1 #include <sys/types.h>
  2 #include <sys/stat.h>
  3 #include <fcntl.h>
  4
  5 #define FIFO_PATH "/tmp/my_fifo"
  6
  7 int main()
  8 {
  9    int ret = 0;
 10
 11    ret = mkfifo(FIFO_PATH, 0x777);
 12    if (ret < 0) {
 13        perror("make fifo error");
 14        return -1;
 15     }
 16
 17    return 0;
 18 }

編譯、運行之後,可以通過查看創建的FIFO文件

[tuzhutuzhu@CentOS fifo]$ll /tmp/my_fifo
pr-xrwSr-t. 1tuzhutuzhu tuzhutuzhu 0 Mar 15 10:26 /tmp/my_fifo

通過上述程序,我們可以看出:創建了有名管道之後,就可以在系統中看到FIFO對應的管道文件。

這也就是FIFO被稱爲有名的原因。也是因爲有了名字,其他非親緣關係的進程也就可以通過名字對其進行訪問。

值得注意的是,FIFO嚴格遵循先進先出(first in first out),對管道及FIFO的讀總是從開始處返回數據,
寫則把數據添加到末尾。它們不支持諸如lseek()等文件定位操作。

2.FIFO的打開與讀寫規則:

2.1 FIFO的打開規則:

如果當前打開操作是爲讀而打開FIFO時,若已經有相應進程爲寫而打開該FIFO,則當前打開操作將成功返回;
否則,可能阻塞直到有相應進程爲寫而打開該FIFO(當前打開操作設置了阻塞標誌);或者,成功返回(當前打開操作沒有設置阻塞標誌)。
如果當前打開操作是爲寫而打開FIFO時,如果已經有相應進程爲讀而打開該FIFO,則當前打開操作將成功返回;
否則,可能阻塞直到有相應進程爲讀而打開該FIFO(當前打開操作設置了阻塞標誌);或者,返回ENXIO錯誤(當前打開操作沒有設置阻塞標誌)。

2.2 FIFO中讀取數據:

約定:如果一個進程爲了從FIFO中讀取數據而阻塞打開FIFO,那麼稱該進程內的讀操作爲設置了阻塞標誌的讀操作。
如果有進程寫打開FIFO,且當前FIFO內沒有數據,則對於設置了阻塞標誌的讀操作來說,將一直阻塞。
對於沒有設置阻塞標誌讀操作來說則返回-1,當前errno值爲EAGAIN,提醒以後再試。
對於設置了阻塞標誌的讀操作說,造成阻塞的原因有兩種:當前FIFO內有數據,但有其它進程在讀這些數據;另外就是FIFO內沒有數據。
解阻塞的原因則是FIFO中有新的數據寫入,不論信寫入數據量的大小,也不論讀操作請求多少數據量。
讀打開的阻塞標誌只對本進程第一個讀操作施加作用,如果本進程內有多個讀操作序列,則在第一個讀操作被喚醒並完成讀操作後,
其它將要執行的讀操作將不再阻塞,即使在執行讀操作時,FIFO中沒有數據也一樣(此時,讀操作返回0)。
如果沒有進程寫打開FIFO,則設置了阻塞標誌的讀操作會阻塞。
注:如果FIFO中有數據,則設置了阻塞標誌的讀操作不會因爲FIFO中的字節數小於請求讀的字節數而阻塞,此時,讀操作會返回FIFO中現有的數據量。

2.3 FIFO中寫入數據:

約定:如果一個進程爲了向FIFO中寫入數據而阻塞打開FIFO,那麼稱該進程內的寫操作爲設置了阻塞標誌的寫操作。
對於設置了阻塞標誌的寫操作:
當要寫入的數據量不大於PIPE_BUF時,linux將保證寫入的原子性。如果此時管道空閒緩衝區不足以容納要寫入的字節數,
則進入睡眠,直到當緩衝區中能夠容納要寫入的字節數時,纔開始進行一次性寫操作。
當要寫入的數據量大於PIPE_BUF時,linux將不再保證寫入的原子性。FIFO緩衝區一有空閒區域,寫進程就會試圖向管道寫入數據,寫操作在寫完所有請求寫的數據後返回。
對於沒有設置阻塞標誌的寫操作:
當要寫入的數據量大於PIPE_BUF時,linux將不再保證寫入的原子性。在寫滿所有FIFO空閒緩衝區後,寫操作返回。
當要寫入的數據量不大於PIPE_BUF時,linux將保證寫入的原子性。如果當前FIFO空閒緩衝區能夠容納請求寫入的字節數,寫完後成功返回;
如果當前FIFO空閒緩衝區不能夠容納請求寫入的字節數,則返回EAGAIN錯誤,提醒以後再寫。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章