簡 述: 在上一篇中,寫了有血緣關係的進程間的通信,使用匿名管道 pipe,本篇是介紹,對於無血緣關係的進程間通信,可以採用(有名)管道的 fifo
方式。
編程環境:
💻: uos20
📎 gcc/g++ 8.3
📎 gdb8.0
💻: MacOS 10.14
📎 gcc/g++ 9.2
📎 gdb8.3
進程間通信 IPC:
進程間通信(Inter Process Communication),字母首寫即爲 IPC。
- 進程間常用的 4 種方式:
- 管道 (簡單)
- 信號 (系統開銷小)
- 共享映射區 (有無血緣關係的進程間通信都可以)
- 本地 socket 套接字 (穩定)
有名管道(fifo):
int fifo(int fildes[2]);
適用於無血緣關係的進程之間的通信。進程之間是不要使用 sleep() 函數的,因爲管道默認就是堵塞的。雖然實現形態上是文件,但是管道本身並不佔用磁盤或者其他外部存儲的空間。在Linux的實現上,它佔用的是內核的一段緩衝區空間。
- 本質:
- 是內核緩衝區。也是僞文件(不佔用磁盤空間)
- 特點:
- 有名管道
- 在磁盤上有這樣一個文件
ls -l -> p
- 僞文件,在磁盤大小永遠爲 0
- 在內核中有一個對應的緩衝區
- 使用半雙工的通信方式
- 使用場景:
- 適用於沒有血緣關係的進程間的通信
- 創建方式:
- 命令: mkfifo 管道名
- 函數: mkfifo()
- fifo 文件可以使用 IO 函數進行操作
- open() / close()
- read() / write()
- 不能執行 lseek 操作
寫一個例子:
有兩個進程 a 和 b,分別用 write.cpp 和 read.cpp 進行實現,然後進程 a 往一個管道里面寫數據,進程 b 從這個管道里面讀取數據。
-
代碼實現:
這裏寫
writ.cpp
,之後運行可執行程序 writ,當做 a 進程 ,往這個管道里面一直寫數據:#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { if (argc < 2) { printf("請按照格式輸入: ./write myfifo\n"); _exit(1); } int ret = access(argv[1], F_OK); //判斷文件是否存在 if (ret == -1) { int r = mkfifo(argv[1], 0777); if (r == -1) { perror("[creator fifo file] "); _exit(1); } else { printf("creator fifo success: %d\n", argv[1]); } } int fd = open(argv[1], O_WRONLY); if (fd == -1) { perror("[open file] "); _exit(1); } char const *p = "this is 2020-04-01, code"; while (true) { // sleep(2); //用來驗證管道阻塞這一屬性的 int len = write(fd, p, strlen(p) + 1); } close(fd); return 0; }
這裏寫
read.cpp
,之後運行可執行程序 read,當做 b 進程 ,往這個管道里面一直讀數據:#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> int main(int argc, char *argv[]) { if (argc < 2) { printf("請按照格式輸入: ./read myfifo\n"); exit(1); } int ret = access(argv[1], F_OK); //判斷文件是否存在 if (ret == -1) { int r = mkfifo(argv[1], 0777); if (r == -1) { perror("[creator fifo file] "); exit(1); } else { printf("creator fifo success: %d\n", argv[1]); } } int fd = open(argv[1], O_RDONLY); if (fd == -1) { perror("[open file] "); exit(1); } char buf[512]; while (true) { int len = read(fd, buf, sizeof(buf)); buf[len] = '\0'; printf("buf = %s, len = %d\n", buf, len); } close(fd); return 0; }
-
代碼分析:
爲了清晰演示管道的阻塞屬性,我們打開寫端的sleep(2)
這一行代碼,故意用來製造寫端的速度比讀端要慢,然後觀察讀端的打印顯示。可以清楚的得到管道是阻塞的這一結論。 -
運行效果:
下載地址:
歡迎 star 和 fork 這個系列的 linux 學習,附學習由淺入深的目錄。