命名管道(FIFO)不同於無名管道之處在於它提供了一個路徑名與之關聯,以 FIFO 的文件形式存在於文件系統中,這樣,即使與 FIFO 的創建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過 FIFO 相互通信,因此,通過 FIFO 不相關的進程也能交換數據。
命名管道(FIFO)和無名管道(pipe)有一些特點是相同的,不一樣的地方在於:
1、FIFO 在文件系統中作為一個特殊的文件而存在,但 FIFO 中的內容卻存放在內存中。
2、當使用 FIFO 的進程退出后,FIFO 文件將繼續保存在文件系統中以便以后使用。
3、FIFO 有名字,不相關的進程可以通過打開命名管道進行通信。
int mkfifo(const char *pathname, mode_t mode);用於創建一個管道
int open(const char *pathname, int flags);用於打開一個管道
打開FIFO文件和普通文件的區別有2點:
第一個是不能以O_RDWR模式打開FIFO文件進行讀寫操作。這樣做的行為是未定義的。
因為我們通常使用FIFO只是為了單向傳遞數據,所以沒有必要使用這個模式。
如果確實需要在程序之間雙向傳遞數據,最好使用一對FIFO或管道,一個方向使用一個。或者采用先關閉在重新打開FIFO的方法來明確改變數據流的方向。
第二是對標志位的O_NONBLOCK選項的用法。
使用這個選項不僅改變open調用的處理方式,還會改變對這次open調用返回的文件描述符進行的讀寫請求的處理方式。
O_RDONLY、O_WRONLY和O_NONBLOCK標志共有四種合法的組合方式:
- flags=O_RDONLY:open將會調用阻塞,除非有另外一個進程以寫的方式打開同一個FIFO,否則一直等待。
- flags=O_WRONLY:open將會調用阻塞,除非有另外一個進程以讀的方式打開同一個FIFO,否則一直等待。
- flags=O_RDONLY|O_NONBLOCK:如果此時沒有其他進程以寫的方式打開FIFO,此時open也會成功返回,此時FIFO被讀打開,而不會返回錯誤。
- flags=O_WRONLY|O_NONBLOCK:立即返回,如果此時沒有其他進程以讀的方式打開,open會失敗打開,此時FIFO沒有被打開,返回-1。
open函數調用中的參數標志O_NONBLOCK會影響FIFO的讀寫操作。
規則如下:
- 對一個空的阻塞的FIFO的read調用將等待,直到有數據可以讀的時候才繼續執行/
- 對一個空的非阻塞的FIFO的read調用立即返回0字節。
- 對一個完全阻塞的FIFO的write調用將等待,直到數據可以被寫入時才開始執行。
- 系統規定:如果寫入的數據長度小於等於PIPE_BUF字節,那么或者寫入全部字節,要么一個字節都不寫入。
注意這個限制的作用:
當只使用一個FIF並允許多個不同的程序向一個FIFO讀進程發送請求的時候,為了保證來自不同程序的數據塊 不相互交錯,即每個操作都原子化,這個限制就很重要了。如果能夠包子所有的寫請求是發往一個阻塞的FIFO的,並且每個寫請求的數據長父小於等於PIPE_BUF字節,系統就可以確保數據絕不會交錯在一起。通常將每次通過FIFO傳遞的數據長度限制為PIPE_BUF是一個好辦法。
- 在非阻塞的write調用情況下,如果FIFO 不能接收所有寫入的數據,將按照下面的規則進行:
- 請求寫入的數據的長度小於PIPE_BUF字節,調用失敗,數據不能被寫入。
- 請求寫入的數據的長度大於PIPE_BUF字節,將寫入部分數據,返回實際寫入的字節數,返回值也可能是0。
其中。PIPE_BUF是FIFO的長度,它在頭文件limits.h中被定義。在linux或其他類UNIX系統中,它的值通常是4096字節。
上面都是網上找的資料,有時間再整理吧,下面是自己寫的一個測試代碼:
write.c
#include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define FIFO_NAME "/tmp/myfifo" int main(){ if(access(FIFO_NAME,F_OK) != 0){//如果文件存在 int err = mkfifo(FIFO_NAME,0777); if(err != 0){ perror("Create fifo failed"); return -1; } } printf("create fifo succeed!\n"); int fifo_fd = open(FIFO_NAME,O_WRONLY); printf("open fifo succeed!\n"); if(fifo_fd < 0){ printf("open fifo failed!\n"); return -1; } int i = 1; for(;i < 100; i++){ if(write(fifo_fd,&i,sizeof(int)) != -1) sleep(1); else perror("Write failed"); } printf("write succeed: %d\n",i); close(fifo_fd); return 0; }
read.c
#include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #define FIFO_NAME "/tmp/myfifo" int main(){ int fifo_fd = open(FIFO_NAME,O_RDONLY | O_NONBLOCK ); if(fifo_fd < 0){ printf("open fifo failed!\n"); return -1; } int i; sleep(5); while(1){ int size = read(fifo_fd,&i,sizeof(int)); if(size > 0) printf("讀到:%d\n",i); } close(fifo_fd); return 0; }
通信過程中,當所有讀進程退出后,寫進程向命名管道內寫數據時,寫進程也會(收到 SIGPIPE 信號)退出。
本例子中,寫進程退出后,讀進程繼續循環,當再次有寫進程啟動時,讀進程就會再次讀到數據。