前言(前情回顧)
進程君(父進程)在開發出匿名管道這門傳音術后,解決了和自己孩子(子進程)間的溝通問題,父子關系趨于融洽。和孩子溝通后,進程君發現,自己脫離群眾太久了,應該加強和群眾的溝通。但是進程君與眾位道友間沒有血緣關系,無法使用匿名管道進行溝通。于是進程君決定改良匿名管道這種技術,讓天下道友都能與自己暢通無阻的溝通,最終產生了一種無暇的傳音技術——命名管道(FIFO)。
命名管道
我在上一篇文章中提到過:
“兩個沒有血緣關系的進程間可以同時打開相同的文件,進程內部分配對應的文件描述符,映射關系記錄在PCB中,而兩個進程間的fd分配時獨立的,也就是fd在多進程中不是唯一的。當然我們也可以在進程1中向文件1寫入數據,進程2從文件1中讀取數據,構成一個偽管道。”
其實這里面的偽管道就是命名管道的意思。創建一個命名管道就是在Linux文件系統下創建一個特殊的fifo文件。與匿名管道的區別是,匿名管道也是文件,但它對文件系統不可見。而命名管道是一個可以在文件系統中看見的文件。
命名管道的創建:mkfifo()
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
這里的filename是一個字符串,描述了命名管道在文件系統中的路徑,當字符串中沒有‘/’出現是則管道創建在當前目錄下。mode一般為0777,對應一個權限掩碼,這里不做重點。直接寫入即可。
我們先測試一下這個函數,在這里我創建了三個命名管道分別為demo1,demo2,demo3,編譯后運行:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>int main() {int res1 = mkfifo("demo1",0777); // 創建于當前路徑下int res2 = mkfifo("./demo2",0777); // 創建于當前路徑下int res3 = mkfifo("../demo3",0777); // 創建于上一級路徑下while(1);return 0;
}
觀察運行前后的工程目錄
可以發現運行后我們的工程目錄下出現了三個新的文件,這三個文件就是我們的命名管道。這就是命名管道對文件系統是可見的這句話的含義。
相信一些腦洞大開的道友已經有了想法,既然命名管道在文件系統中可見,也就說明所有的進程都可以訪問這個文件,那么只要我向這個進程中讀寫文件,是不是就完成了進程間的通信?
答案是:完全正確
如何實現對文件的讀和寫?不清楚的道友可以去看我之前寫的一篇文章:linux多線(進)程編程——(1)前置知識
我們直接上代碼,為了體現出命名管道與匿名管道的區別,這次我們要真正實現在兩個程序間通信,所以我們要寫兩個C語言源文件。
proc1.c:創建命名管道并且向命名管道內寫入數據
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main() {int res1 = mkfifo("fifo_demo", 0777);int wfd = open("fifo_demo", O_WRONLY); // 只寫char send_buf[20];bzero(send_buf, 20);memcpy(send_buf, "hello, world!", 14);while(1) {write(wfd, send_buf, 20);sleep(1);}return 0;
}
proc2.c:接收管道內的數據
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main() {usleep(500*1000);// int res1 = mkfifo("fifo_demo", 0777);int rfd = open("fifo_demo", O_RDONLY); // 只讀char recv_buf[20];while(1) {bzero(recv_buf, 20);read(rfd, recv_buf, 20);printf("%s\n", recv_buf);sleep(1);}return 0;
}
在命令行中運行程序,其中后面加一個&,表示在后臺運行,讓出終端
lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ gcc -o proc1 proc1.c
lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ gcc -o proc2 proc2.c
lol@qingfenfuqin:~/work/linux_study/pipe/fifo$ ./proc1&
[1] 9729
lockin@qingfenfuqin:~/work/linux_study/pipe/fifo$ ./proc2
hello, world!
hello, world!
hello, world!
hello, world!
...
注意:
(1)在兩個程序中每次寫入和讀取的字節數量保持一致。
(2)在程序編寫時就應該知道管道的名字。
(3)管道是單工通信,開發時不能又讀又寫,僅能以只讀(O_RDONLY)或者只寫(O_WRONLY)打開。
小結
這節課的知識點:
(1)命名管道的創建方法:mkfifo();
(2)命名管道與文件的關系,如何操作命名管道:read();write();
(3)如何在后臺運行一個進程:&。
(4)命名管道與匿名管道的差異,以及對文件系統的可見性。
下一集我們將學習進程間第二中通信方式——共享內存的前置知識:linux多線(進)程編程——(5)虛擬內存與內存映射
結束語
“進程君開發出匿名管道與命名管道后,九天十地的道友終于可以暢通無阻的溝通交流了。”
聽完這個修仙界傳說,不知不覺間你的識海中也多了一道無暇神通,千里傳音術——管道。
祝各位道友早日神功大成。