上一篇講的是管道(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,如close、read、write等等。
下面來創建一個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錯誤,提醒以後再寫。