【Linux系统编程】进程间通信之命名管道
00. 目錄
文章目錄
- 00. 目錄
- 01. 命名管道概述
- 02. 命名管道創建
- 03. 命名管道特性
- 04. 命名管道非阻塞
- 05. 附錄
01. 命名管道概述
無名管道,由于沒有名字,只能用于親緣關系的進程間通信。為了克服這個缺點,提出了命名管道(FIFO),也叫有名管道、FIFO 文件。
命名管道(FIFO)不同于無名管道之處在于它提供了一個路徑名與之關聯,以 FIFO 的文件形式存在于文件系統中,這樣,即使與 FIFO 的創建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過 FIFO 相互通信,因此,通過 FIFO 不相關的進程也能交換數據。
命名管道(FIFO)和無名管道(pipe)有一些特點是相同的,不一樣的地方在于:
1、FIFO 在文件系統中作為一個特殊的文件而存在,但 FIFO 中的內容卻存放在內存中。
2、當使用 FIFO 的進程退出后,FIFO 文件將繼續保存在文件系統中以便以后使用。
3、FIFO 有名字,不相關的進程可以通過打開命名管道進行通信。
02. 命名管道創建
相關函數:
#include <sys/types.h> #include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode); 功能:命名管道的創建。 參數:pathname : 普通的路徑名,也就是創建后 FIFO 的名字。mode : 文件的權限,與打開普通文件的 open() 函數中的 mode 參數相同。(0666) 返回值:成功:0 狀態碼失敗:如果文件已經存在,則會出錯且返回 -1。測試代碼:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h>int main(int argc, char *argv[]) {int ret;ret = mkfifo("fifo", 0666); // 創建命名管道if(ret != 0){ // 出錯perror("mkfifo");}return 0; }測試結果:
deng@itcast:~$ gcc 1.c deng@itcast:~$ ./a.out deng@itcast:~$ ls fifo fifo deng@itcast:~$03. 命名管道特性
我們可以將命名管道當做普通文件一樣進行操作:open()、write()、read()、close()。但是,和無名管道一樣,操作命名管道肯定要考慮默認情況下其阻塞特性。
3.1 打開管道阻塞特性
下面驗證的是默認情況下的特點,即 open() 的時候沒有指定非阻塞標志( O_NONBLOCK )。
open() 以只讀方式打開 FIFO 時,要阻塞到某個進程為寫而打開此 FIFO
open() 以只寫方式打開 FIFO 時,要阻塞到某個進程為讀而打開此 FIFO。
簡單一句話,只讀等著只寫,只寫等著只讀,只有兩個都執行到,才會往下執行。
讀端代碼
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666);if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_RDONLY);//等著只寫if(fd < 0){perror("open fifo");}printf("after open\n");return 0; }寫端代碼:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666);if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_WRONLY); //等著只讀if(fd < 0){perror("open fifo");}printf("after open\n");return 0; }打開兩個終端,只有兩個程序都執行之后,兩個程序才會有輸出。
如果大家不想在 open() 的時候阻塞,我們可以以可讀可寫方式打開 FIFO 文件,這樣 open() 函數就不會阻塞。
// 可讀可寫方式打開 int fd = open("my_fifo", O_RDWR);3.2 有名管道讀寫特性
假如 FIFO 里沒有數據,調用 read() 函數從 FIFO 里讀數據時 read() 也會阻塞。這個特點和無名管道是一樣的。
寫端代碼:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666);//創建命名管道if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_WRONLY); //等著只讀if(fd < 0){perror("open fifo");}printf("after open\n");printf("before write\n");// 5s后才往命名管道寫數據,沒數據前,讀端read()阻塞sleep(5);char send[100] = "Hello Mike";write(fd, send, strlen(send));printf("write to my_fifo buf=%s\n", send);return 0; }讀端代碼:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666); //創建命名管道if(ret != 0){perror("mkfifo");}printf("before open\n");fd = open("my_fifo", O_RDONLY);//等著只寫if(fd < 0){perror("open fifo");}printf("after open\n");printf("before read\n");char recv[100] = {0};//讀數據,命名管道沒數據時會阻塞,有數據時就取出來read(fd, recv, sizeof(recv)); printf("read from my_fifo buf=[%s]\n", recv);return 0; }通信過程中若寫進程先退出了,就算命名管道里沒有數據,調用 read() 函數從 FIFO 里讀數據時不阻塞;若寫進程又重新運行,則調用 read() 函數從 FIFO 里讀數據時又恢復阻塞。
寫端代碼:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666); // 創建命名管道if(ret != 0){perror("mkfifo");}fd = open("my_fifo", O_WRONLY); // 等著只讀if(fd < 0){perror("open fifo");}char send[100] = "Hello Mike";write(fd, send, strlen(send)); //寫數據printf("write to my_fifo buf=%s\n",send);while(1); // 阻塞,保證讀寫進程保持著通信過程return 0; }讀端代碼:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666);// 創建命名管道if(ret != 0){perror("mkfifo");}fd = open("my_fifo", O_RDONLY);// 等著只寫if(fd < 0){perror("open fifo");}while(1){char recv[100] = {0};read(fd, recv, sizeof(recv)); // 讀數據printf("read from my_fifo buf=[%s]\n",recv);sleep(1);}return 0; }通信過程中,讀進程退出后,寫進程向命名管道內寫數據時,寫進程也會(收到 SIGPIPE 信號)退出。
調用 write() 函數向 FIFO 里寫數據,當緩沖區已滿時 write() 也會阻塞。
這兩個特點和無名管道是一樣的,這里不再驗證。
04. 命名管道非阻塞
命名管道可以以非阻塞標志(O_NONBLOCK)方式打開:
fd = open("my_fifo", O_WRONLY | O_NONBLOCK); fd = open("my_fifo", O_RDONLY | O_NONBLOCK);非阻塞標志(O_NONBLOCK)打開的命名管道有以下特點:
1、先以只讀方式打開,如果沒有進程已經為寫而打開一個 FIFO, 只讀 open() 成功,并且 open() 不阻塞。
2、先以只寫方式打開,如果沒有進程已經為讀而打開一個 FIFO,只寫 open() 將出錯返回 -1。
3、read()、write() 讀寫命名管道中讀數據時不阻塞。
測試代碼
寫端代碼:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666); // 創建命名管道if(ret != 0){perror("mkfifo");}// 只寫并指定非阻塞方式打開fd = open("my_fifo", O_WRONLY|O_NONBLOCK);if(fd<0){perror("open fifo");}char send[100] = "Hello Mike";write(fd, send, strlen(send));printf("write to my_fifo buf=%s\n",send);while(1);return 0; }讀端代碼:
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main(int argc, char *argv[]) {int fd;int ret;ret = mkfifo("my_fifo", 0666); // 創建命名管道if(ret != 0){perror("mkfifo");}// 只讀并指定非阻塞方式打開fd = open("my_fifo", O_RDONLY|O_NONBLOCK);if(fd < 0){perror("open fifo");}while(1){char recv[100] = {0};read(fd, recv, sizeof(recv));printf("read from my_fifo buf=[%s]\n",recv);sleep(1);}return 0; }05. 附錄
總結
以上是生活随笔為你收集整理的【Linux系统编程】进程间通信之命名管道的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux系统编程】进程间通信之无名管
- 下一篇: 【Linux系统编程】进程间通信之消息队