程序間通信———管道
什麼是程序間通信
程序間通信(IPC,Interprocess communication)是一組程式設計接口,讓程式員能夠協調不同的程序,使之能在一個作業系統裡同時運作,并互相傳遞、交換資訊。這使得一個程式能夠在同一時間裡處理許多使用者的要求。因為即使隻有一個使用者發出要求,也可能導緻一個作業系統中多個程序的運作,程序之間必須互相通話。舉個例子來說,也就是說用一些方法讓兩個本來不能見面的人見面。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。
那麼管道建立好了,它到底是怎麼進行通信的呢?下面先用圖檔來說明一下。
#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++;
}
}
}
運作結果如下圖
這樣就實作了程序間通信。(單向通信)父程序讀,子程序寫。
使用管道需要注意以下四種特殊情況(假設都是阻塞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 ;
}
下面是運作結果
運作就可以看到實作了程序間的通信。