普通的進程間通信方式--有名管道fifo(適用於無血緣關係的進程)



簡 述: 在上一篇中,寫了有血緣關係的進程間的通信,使用匿名管道 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) 這一行代碼,故意用來製造寫端的速度比讀端要慢,然後觀察讀端的打印顯示。可以清楚的得到管道是阻塞的這一結論。

  • 運行效果:


下載地址:

11_pipe

歡迎 star 和 fork 這個系列的 linux 學習,附學習由淺入深的目錄。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章