在前一篇文章中,我們講解了如何使用匿名管道來在進程之間傳遞數據,同時也看到了這個方式的一個缺陷,就是這些進程都由一個共同的祖先進程啟動,這給我們在不相關的的進程之間交換數據帶來了不方便。這裡將會介紹進程的另一種通信方式——命名管道,來解決不相關進程間的通信問題。
什麼是命名管道 命名管道也被稱為FIFO文件,它是一種特殊類型的文件,它在文件系統中以文件名的形式存在,但是它的行為卻和之前所講的沒有名字的管道(匿名管道)類似。 由於Linux中所有的事物都可被視為文件,所以對命名管道的使用也就變得與文件操作非常的統一,也使它的使用非常方便,同時我們也可以像平常的文件名一樣在命令中使用。FIFO只是借用了文件系統(file system,命名管道是一種特殊類型的文件,因為Linux中所有事物都是文件,它在文件系統中以文件名的形式存在。)來為管道命名。寫模式的進程向FIFO文件中寫入,而讀模式的進程從FIFO文件中讀出。當刪除FIFO文件時,管道連接也隨之消失。FIFO的好處在於我們可以通過文件的路徑來識別管道,從而讓沒有親緣關系的進程之間建立連接 創建一個命名管道
#includemknod函數也可以創建一個命名管道,但是mknod是比較老的函數,而使用mkfifo函數更加簡單和規范,所以建議在可能的情況下,盡量使用mkfifo而不是mknod。#include int mkfifo(const char *pathname, mode_t mode); //實例 int main() { if (mkfifo("p2", 0644) == -1) err_exit("mkfifo error"); }
FIFO與PIPE的區別:
1) 匿名管道由pipe函數創建並打開。
命名管道由mkfifo函數創建,打開用open。
2) FIFO(命名管道)與pipe(匿名管道)之間唯一的區別在它們創建與打開的方式不同,一但這些工作完成之後,它們具有相同的語義。
打開FIFO文件 與打開其他文件一樣,FIFO文件也可以使用open調用來打開。注意,mkfifo函數只是創建一個FIFO文件,要使用命名管道還是將其打開。 但是有兩點要注意,1、就是程序不能以O_RDWR模式打開FIFO文件進行讀寫操作,而其行為也未明確定義,因為如一個管道以讀/寫方式打開,進程就會讀回自己的輸出,同時我們通常使用FIFO只是為了單向的數據傳遞。2、就是傳遞給open調用的是FIFO的路徑名,而不是正常的文件。 打開FIFO文件通常有4種方式:open(const char *path, O_RDONLY);//1 open(const char *path, O_RDONLY | O_NONBLOCK);//2 open(const char *path, O_WRONLY);//3 open(const char *path, O_WRONLY | O_NONBLOCK);//4
下面這個例子介紹 兩個進程通過FIFO對拷數據:利用管道,兩個進程間進行文件復制。
1.進程writefifo:
(1)讀文件(文件名從命令行參數中獲取)
(2)寫入管道myFifo(管道由該進程創建)
(3)關閉文件及管道
2.進程readfifo:
(1)讀管道myFifo
(2)寫入文件[該文件有進程創建並打開]
(3)關閉文件
(4)刪除管道
//1:writefifo int main(int argc, char *argv[]) { if (argc < 2) err_quit("Usage: ./writefifo"); // 創建管道 if (mkfifo("myFifo", 0644) == -1) err_exit("mkfifo error"); int outfd = open("myFifo", O_WRONLY); //打開FIFO int infd = open(argv[1], O_RDONLY); //打開文件 if (outfd == -1 || infd == -1) err_exit("open file/fifo error"); char buf[BUFSIZ]; int readBytes; while ((readBytes = read(infd, buf, sizeof(buf))) > 0) { write(outfd, buf, readBytes); } close(infd); close(outfd); }
//2:readfifo int main(int argc, char *argv[]) { if (argc < 2) err_quit("Usage: ./writefifo分析:兩個程序都使用阻塞模式的FIFO,為了讓大家更清楚地看清楚阻塞究竟是怎麼一回事,首先我們運行writefifo,並把它放到後台去運行。這時調用jobs命令,可以看到它確實在後台運行著,過了5秒後,再調用jobs命令,可以看到進程writefifo還沒有結束,它還在繼續運行。因為writefifo進程的open調用是阻塞的,在readfifo還沒有運行時,也就沒有其他的進程以讀方式打開同一個FIFO,所以它就一直在等待,open被阻塞,沒有返回。然後,當我們進程readfifo運行時(為了查看性能,在time命令中運行),writefifo中的open調用返回,進程開始繼續工作,然後結束進程。而readfifo的open調用雖然也是阻塞模式,但是writefifo早已運行,即早有另一個進程以寫方式打開同一個FIFO,所以open調用立即返回。"); int outfd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0644); //創建並打卡文件 int infd = open("myFifo", O_RDONLY); //打開FIFO if (infd == -1 || outfd == -1) err_exit("open file/fifo error"); char buf[BUFSIZ]; int readBytes; while ((readBytes = read(infd, buf, sizeof(buf))) > 0) { write(outfd, buf, readBytes); } close(outfd); unlink("myFifo"); //刪除FIFO }