【Linux】進程間通信 - 管道

       進程間通信(IPC,Interprocess communication)是一組編程接口,讓程序員能夠協調不同的進程,使之能在一個操作系統裏同時運行,並相互傳遞、交換信息。這使得一個程序能夠在同一時間裏處理許多用戶的要求。因爲即使只有一個用戶發出要求,也可能導致一個操作系統中多個進程的運行,進程之間必須互相通話。IPC接口就提供了這種可能性。每個IPC方法均有它自己的優點和侷限性,一般,對於單個程序而言使用所有的IPC方法是不常見的。

進程間通信目的:

    1.數據傳輸:一個進程需要將它的數據發送給另一個進程
    2.資源共享:多個進程之間共享同一份資源
    3.通知事件:一個進程需要向另一個或一組進程發送消息,通知他們發生了某種事件
    4.進程控制:有些進程希望完全控制另一給進程的執行(如Debug進程),此時控制希望能夠攔截另一個進程的所有陷入和異常,並能夠即使知道它的狀態改變。

管道:

管道它是Unix中最古老的進程間通信的形式。我們把從一個進程連接到另一個進程的一個數據流稱爲一個“管道”。我們都知道在Linux下“一切皆文件”,其實這裏的管道就是一個文件。管道實現進程通信就是讓兩個進程都能訪問該文件。管道分爲匿名管道和命名管道。

【匿名管道】

#include<unistd.h>  
功能:創建一個無名管道  
原型  
  int pipe(int fd[2]);  
參數  
  fd:文件描述符數組,其中fd[0]表示讀端,fd[1]表示寫端  
返回值:成功返回0,失敗返回錯誤代碼

管道的特徵: 
1、只提供單向通信,也就是說,兩個進程都能訪問這個文件,假設進程1往文件內寫東西,那麼進程2 就只能讀取文件的內容。 
2、只能用於具有血緣關係的進程間通信,通常用於父子進程建通信 。
3、管道是基於字節流來通信的 。
4、依賴於文件系統,它的生命週期隨進程的結束結束(隨進程)。 

5、其本身自帶同步互斥效果。

實例:從鍵盤讀取數據,寫入管道,讀取管道,寫到屏幕

#include<stdio.h>    
#include<stdlib.h>    
#include<unistd.h>    
#include<string.h>    
    
int main(void)    
{    
    int fds[2];    
    char buf[100];    
    int len;    
    if (pipe(fds) == -1)    
        perror("make pipe"), exit(1);    
    //讀取數據    
    while (fgets(buf, 100, stdin)){    
        len = strlen(buf);    
    //寫入管道    
        if (write(fds[1], buf, len) != len){    
            perror("write to pipe");    
            break;    
        }    
        memset(buf, 0x00, sizeof(buf));    
    //讀取管道    
        if ((len = read(fds[0], buf, 100)) == -1){    
            perror("read from pipe");    
            break;    
        }    
    //寫到屏幕    
        if (write(1, buf, len) != len){    
            perror("write to stdout");    
            break;    
        }    
    }    
}  

管道的讀寫規則:

    1、寫端的文件描述符關閉,讀端一直讀。

    2、寫端的文件描述符沒關閉,但寫端也不寫數據,而讀端一直讀。

    3、寫端一直寫數據,讀端不讀,也不關閉它的文件描述符。

    4、寫端寫數據,而讀端的文件描述符關閉。

【命名管道】(named pipe 或 FIFO

 管道應用的一個限制就是只能在具有相同祖先(具有親緣關係)的進程間通信。 

 如果我們想在不相關的進程之間交換數據,可以使用FIFO文件來做這項工作,它經常被稱爲命名管道。

1、與匿名管道的區別:提供了一個路徑名與之關聯,以FIFO文件的形式存儲於文件系統中,能夠實現任何兩個進程之間通信。而匿名管道對於文件系統是不可見的,它僅限於在父子進程之間的通信。 
2、 FIFO是一個設備文件,在文件系統中以文件名的形式存在,因此即使進程與創建FIFO的進程不存在血緣關係也依然可以通信,前提是可以訪問該路徑。 
3、 FIFO(first input first output)總是遵循先進先出的原則,即第一個進來的數據會第一個被讀走。

【命名管道的創建】:

1.可在命令行上創建: 
$ mkfifo filename

2.在程序裏創建: 
int mkfifo (const char* filename , mode_t mode); 

註釋:filename 爲管道名稱;mode 爲權限

實例:用命名管道實現client/server通信:

首先創建Makefile文件:

.PHONY:all  
all:client server  
client:client.c  
    gcc -o $@ $^  
server:server.c  
    gcc -o $@ $^  
  
.PHONY:clean  
clean:  
    rm -f client server  

client.c

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <string.h>  
  
  
int main()  
{  
    int wfd = 0;  
    if((wfd = open("mypipe",O_WRONLY))==-1)//以只寫方式打開管道  
    {  
        perror("open");  
        return 1;  
    }  
    char buf[1024];  
    while(1)  
    {  
        printf("Please Enter:");  
        //從標準輸入讀取數據  
        fflush(stdout);  
        size_t s = read(0,buf,sizeof(buf)-1);  
        if(s>0)  
        {  
            buf[s]=0;  
            write(wfd,buf,strlen(buf));//將讀取到的數據寫進管道  
        }  
        else if(s==-1)  
        {  
            perror("write");  
            return 2;  
        }  
    }  
    return 0;  
} 

server.c

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <fcntl.h>  
#include <string.h>  
  
  
int main()  
{  
    if(mkfifo("mypipe",0644)==-1)//創建命名管道  
    {  
        perror("mkfifo");  
        return 1;  
    }  
    int rfd = 0;  
    if((rfd = open("mypipe",O_RDONLY))==-1)//以只讀方式打開管道  
    {  
        perror("open");  
        return 2;  
    }  
    char buf[1024];  
    while(1)  
    {  
        ssize_t s = read(rfd,buf,sizeof(buf)-1);//從管道中讀數據  
        if(s>0)  
        {  
            buf[s-1]=0;  
            printf("client say:%s\n",buf);  
        }  
        else if(s==0)  
        {  
            printf("client quit!!!\n");  
            break;  
        }  
        else  
        {  
            perror("read");  
            return 3;  
        }  
    }  
    return 0;  
}  

輸入make編譯代碼,然後輸入./server ,這時就會創建一個管道,然後再打開一個終端,在這個新的終端上輸入./client,就可以進行通信。如下圖所示:


發佈了85 篇原創文章 · 獲贊 110 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章