天天看點

IPC之---管道

程序間通信———管道

什麼是程序間通信

程序間通信(IPC,Interprocess communication)是一組程式設計接口,讓程式員能夠協調不同的程序,使之能在一個作業系統裡同時運作,并互相傳遞、交換資訊。這使得一個程式能夠在同一時間裡處理許多使用者的要求。因為即使隻有一個使用者發出要求,也可能導緻一個作業系統中多個程序的運作,程序之間必須互相通話。舉個例子來說,也就是說用一些方法讓兩個本來不能見面的人見面。IPC接口就提供了這種可能性。那麼不同程序之間存在着什麼雙方都可以通路的媒體呢?程序的使用者空間是互相獨立的,一般而言是不能互相通路的,唯一的例外是共享記憶體區。另外,系統空間是“公共場所”,各程序均可以通路,是以核心也可以提供這樣的條件。下面一副圖來說明程序間的通信。

IPC之---管道

程序間通信的方法:主要包括管道, 系統IPC(包括消息隊列,信号,共享存儲), 套接字(SOCKET).(下面主要介紹管道,匿名管道和命名管道)

管道包括以下三種:

<1>普通管道PIPE,特點就是:

1.單向通信。

2.隻有在具有親緣關系的程序間通信(父子,兄弟)。

3.具有同步機制。(寫什麼,讀什麼)

4.他是一種面向位元組流的通信服務。

5.生命周期随程序(也就是說,當與之相關的程序退出時,管道内申請的空間不管有沒有釋放系統都會釋放它;就是說隻要程序退出了,管道也就随之退出。比如,在管道中申請了堆空間,隻申請不釋放,當管道關閉,也就是程序退出了,系統就會自動是放那些空間。)

<2>流管道s_pipe: 特點:可以雙向傳輸.其它與普通管道相同。

<3>命名管道:name_pipe,可以在許多并不相關的程序之間進行通訊.其它與普通管道相同。

匿名管道

由pipe函數建立

#include<stdio.h>
int pipe(int fileds[]);
           

調用pipe函數時在記憶體中開辟一塊緩沖區(就稱為管道),用于通信,它有一個讀端和一個寫端,通過函數參數傳給使用者程式兩個檔案描述符,fileds[0]指向管道的讀端,fileds[1]指向管道的寫端。(友善記憶就可以了解為,标準輸入0,标準輸出1)看起來管道就像是一個打開的檔案,

通過read(fileds[0]),和write(fileds[1])往裡面寫和讀, 從上面的圖我們可以看出,也就是在讀寫核心緩沖區。

pipe函數建立管道成功傳回0,失敗傳回-1。

那麼管道建立好了,它到底是怎麼進行通信的呢?下面先用圖檔來說明一下。

IPC之---管道
#include<stdio.h>
 #include<unistd.h>
 #include<errno.h>
 #include<string.h>
 int main()
 {
     int _pipe[];//定義參數
     int ret = pipe(_pipe);
     if(ret==-)//建立管道失敗
     {
         printf("creat pipe error!errno code is :%d\n",errno);//錯誤碼
         return ;//傳回值,這樣你就會知道到底是哪裡出現了錯誤
     }
     pid_t id = fork();
     if(id<)//建立子程序失敗
     {
         printf("fork error!");
         return ;
     }
     else if(id==)
     {
         //child
         close(_pipe[]);//關閉讀端
         int i = ;
         char *_mesg = NULL;
         while(i<)
         {
             _mesg = "I am child!";
             write(_pipe[],_mesg,strlen(_mesg)+);//xie
             sleep();
             i++;
         }
     }
     else
     {
         //father
         close(_pipe[]);//關閉寫端
         char _mesg_c[];
         int j = ;
         while(j<)
         {
             memset(_mesg_c,'\0',sizeof(_mesg_c));
             read(_pipe[],_mesg_c,sizeof(_mesg_c));
             printf("%s\n",_mesg_c);
             j++;
         }
     }
 }
           

運作結果如下圖

IPC之---管道

這樣就實作了程序間通信。(單向通信)父程序讀,子程序寫。

使用管道需要注意以下四種特殊情況(假設都是阻塞I/O操作,沒有設定O_NONBLOCK标志)

1. 如果所有指向管道寫端的檔案描述符都關閉了,(管道寫端的引用計數為0),而仍然有程序從管道的讀端讀取資料,那麼管道中剩餘的資料都被讀取之後,再次read将會傳回0,就像讀到檔案結尾一樣。也就是說,寫端不會寫,讀端讀完之後就會再等着寫端去寫,但是寫端關閉了啊,不會寫了,是以就出現上面說的情況。這就展現出了管道的同步機制。

2. 如果有指向管道寫端的檔案描述符沒有關閉,(管道寫端的引用計數大于0)而持有管道寫端的程序也沒有向管道中寫資料,這時有程序管道讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會阻塞,直到管道中有資料可讀了才讀取資料并傳回。通俗講就是,讀端讀資料,一直讀,但是寫端不寫了,而且寫端并沒有關閉,是以這時讀端就會一直等着寫端去寫。這就造成了阻塞式等待。

3. 如果所有指向管道讀端的檔案描述符都關閉了(管道讀端的引用計數為0),這時有程序向管道的寫端寫資料,那麼該程序會收到SIGPIPE,通常會導緻程序異常終止。是以程序就會異常退出了。

4. 如果有指向管道讀端的檔案描述符沒關閉(管道讀端的引用計數大于0)而持有管道讀端的程序也沒有從管道中讀取資料,這時有程序向管道寫端寫資料,那麼在管道寫滿時再寫将會阻塞,直到管道中有了空位置才寫入并傳回,也就是管道的同步機制。

命名管道(FIFO)

命名管道(NamedPipe)是伺服器程序和一個或多個客戶程序之間通信的單向或雙向管道。不同于匿名管道的是:命名管道可以在不相關的程序之間和不同計算機之間使用,伺服器建立命名管道時給它指定一個名字,任何程序都可以通過該名字打開管道的另一端,根據給定的權限和伺服器程序通信。而且,FIFO總是按照先進先出的原則工作,第一個被寫入的資料首先從管道中讀出。

命名管道的建立:

#include<sys/types.h>
#include<sys/stat.h>
int mknod(const char*path,mode_t mod,dev_t dev);
int mkfifo(const char*path,mode_t mode);
           

函數參數中的path為建立的命名管道的路徑名,mod為建立命名管道的模式,指明其存取權限,dev為裝置值,該值檔案建立的種類,它隻在建立裝置檔案時才會用到。這兩個函數帶哦用成功傳回0,失敗都傳回-1.線面用mknod函數建立一個命名管道

umask();//重置管道的存取權限
if(mknod("/tmp/fifo",S_IFIFO|)==-)
{
    perror("mknod error");
    exit();
}
//函數mkfifo的使用代碼
umask();
if(mkfifo("/tmp/fifo",S_IFIFO|)==- )
{
    perror("mkfifo error");
    exit();
}
//"S_IFIFO|0666"緻命建立一個管道的存取權限為0666
           

命名管道的使用和匿名管道基本相同,隻是在使用命名管道之前首先要使用open函數打開,因為命名管道是存在于硬碟上的檔案,而管道是存在于記憶體中的特殊檔案。

需要注意,使用open的幾點:

1. 調用open()打開命名管道可能會被阻塞,但是如果同時用讀寫方式(O_RDWR)打開,則一定不會造成阻塞。

2. 如果以制度方式(O_RDONLY)打開,則調用open()函數的程序将會被阻塞直到有寫才能打開管道。

3. 同樣,以寫方式(O_WRONLY)打開也會阻塞直到有讀方式打開管道。

命名管道可以在不同的程序間通信,形象點講就像我們平時使用的聊天工具一樣,一個寫一個讀。下面用代碼實作。

//client.c 也就是 管道的寫端
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
    umask();
    if(mkfifo("./mypipe", | S_IFIFO)<)
    {
        perror("mkfifo error");
        return ;
    }
    int fd = open("./mypipe",O_RDONLY);
    if(fd<)
    {
        printf("open file error!\n");
        return ;
    }
    char buf[];
    while()
    {
        ssize_t ret = read(fd,buf,sizeof(buf)-);
         if(ret>)//error or end of file
             {
                 buf[ret] = ;
                 printf("client say# %s\n",buf);
             }
         else if(ret==)
         {
             printf("client quit !server begin quit!\n");
             break;
     }

   }
    close(fd);
    return ;
}
           
//server.c 也就是管道的讀端
#include<stdio.h>
 #include<sys/types.h>
 #include<sys/stat.h>
 #include<unistd.h>
 #include<fcntl.h>
 #include<string.h>
 int main()
 {
     int fd = open("./mypipe",O_WRONLY);
     if(fd<)
     {
         printf("open file error!\n");
         return ;
     }
     char buf[];
     while()
     {
         printf("please enter # ");
         fflush(stdout);
         ssize_t ret = read(,buf,sizeof(buf)-);
          if(ret>)
              {
                  buf[ret-] = ;
                  write(fd,buf,strlen(buf));
              }

    }
     close(fd);
     return ;
 }
           

下面是運作結果

IPC之---管道

運作就可以看到實作了程序間的通信。

繼續閱讀