Linux利用管道實現進程間通信

系統中,進程之間獨立性大,互相的影響較小,所以造成進城之間相互通信的難度較大。
所在想辦法讓進程去看到一份相同的公共資源(一般操作系統提供,這資源不屬於任何進程),以這樣的方式實現進程的通信。
用管道實現進程間通信的原理:如下圖所示

匿名管道(pipe)

【匿名管道的創建】
int pipe(fd[2]);
這是一個系統調用接口,創建一個匿名管道文件。
【函數具體說明】
參數:fd是一個具有兩個參數的數組,當成功創建匿名管道時,會用這個數組存放管道文件的文件描述符;fd[0]中保存管道文件的讀端,fd[1]保存管道文件的寫端。
返回值:成功返回0,否則返回-1。

例:子進程和父進程之間的通信
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
int main()
{
    int fd[2] = {0, 0};
    //創建管道,將寫端和讀端的文件描述符分別放入fd[1],fd[0]
    if(pipe(fd) < 0)
    {
    perror("pipe: ");
    return -1;
    }
    pid_t id = fork();
    if(id == 0)//子進程向管道中寫入
    {
    //關閉子進程的讀端
    close(fd[0]);
    char *msg = "Hello,I am child\n";
    while(1)
    {
        //向管道中寫入內容
        write(fd[1], msg, strlen(msg));
        sleep(1);
    }
    }
    else//父進程從管道中讀取
    {
    //關閉父進程的寫端
    close(fd[1]);
    char buf[1024];
    while(1)
    {
        //從管道中讀出內容
        ssize_t s = read(fd[0],buf,sizeof(buf)-1);
        if(s > 0)
        {
        buf[s] = 0;
        printf("father read: %s", buf);
        }
    }
    }
}
【匿名管道的特點】
  1. 單向通信
  2. 只能進行具有血緣關係的進程間通信,常用於父子進程
  3. 管道生命週期隨進程
  4. 管道提供面向字節流的通信服務
  5. 自帶同步機制,保證讀寫的正確性

命名管道(FIFO)
命名管道與匿名管道的區別:
管道的一個不足之處是沒有名字,因此,只能⽤用於具有親緣關係的進程間通信。FIFO不同於管道之處在於它提供一 個路徑名與之關聯,以FIFO的文件形式存儲於文件系統中。命名管道是一個設備文件。因 此,即使進程與創建FIFO的進程不存在親緣關係,只要可以訪問該路徑,就能夠通過FIFO 相互通信。

【命名管道的創建】
int mkfifo(const char *pathname, mode_t mode);
這是一個系統調用接口,創建一個命名管道。
【函數具體說明】
參數:pathname爲創建的命名管道的全路徑名;mod爲創建的命名管道的模式,指 明其存取權限
返回值:成功返回0,否則返回-1

例:server和client兩個不具有血緣關係的進程間的通信

server:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main()
{
    //創建命名管道
    if(mkfifo("./mypipe", S_IFIFO|0666) < 0)
    {
    perror("mkfifo :");
    return -1;
    }
    //代開管道文件,讀方式打開
    int fd = open("./mypipe", O_RDONLY);
    if(fd < 0)
    {
    perror("open :");
    return -2;
    }
    //緩衝
    char buf[1024];
    while(1)
    {
    //從管道輸入讀內容到緩衝
    ssize_t s = read(fd, buf, sizeof(buf)-1);
    if(s > 0)
    {
        buf[s] = 0;
        //從緩衝寫到標準輸出
        write(0,buf,strlen(buf));
    }
    else if(s == 0)
    {
        break;
    }
    }
    close(fd);
    return 0;
}

client:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main()
{
    //代開管道文件,寫方式打開
    int fd = open("./mypipe", O_WRONLY);
    if(fd < 0)
    {
    perror("open :");
    return -2;
    }
    //緩衝
    char buf[1024];
    while(1)
    {
    //從標準輸入讀內容到緩衝
    ssize_t s = read(0, buf, sizeof(buf)-1);
    if(s > 0)
    {
        buf[s] = 0;
        //從緩衝寫到管道中
        write(fd,buf,strlen(buf));
    }
    else if(s == 0)
    {
        break;
    }
    }
    close(fd);
    return 0;
}

【命名管道的特點】
     命名管道的特點與匿名管道的特點類似,有一點不同的是,命名管道可以讓沒有血緣關係的進程之間進行通信,而匿名管道不行

【使用管道時需要注意的四種情況】

  1. 如果指向管道寫端的文件描述符被關閉,那麼當其他進程繼續從讀端讀取數據時,管道中剩下的數據被取走之後在繼續讀取數據會直接返回0,類似於讀到文件末尾。
  2. 如果指向管道寫端的文件描述符沒有關閉,但是進程並不往其中寫入內容,當其他進程繼續從讀端讀取數據時,管道中剩下的數據被讀取之後在進行讀會read阻塞,直到管道中寫入新的數據。
  3. 如果指向管道讀端的文件描述符沒有關閉,但是進程並不讀任何內容,當其他進程繼續從寫端寫入時,當管道中剩下的空間被寫滿在次寫入會write阻塞,直到管道中的數據被讀走。
  4. 如果指向管道讀端的文件描述符被關閉,當寫端進程繼續向管道中寫數據時,操作系統會向寫端的進程發送13號信號,導致進程異常結束。
【管道的大小】
可以使用 ulimit -a來查看

可以看到管道的大小爲 512*8 = 4096bytes
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章