Linux下的程序間通訊之管道
在Linux下,每個程序各自有不同的使用者地址空間,任何一個程序的全域性變數在另一個程序中都看不到所以進 程之間要交換資料必須通過核心,在核心中開闢一塊緩衝區,程序1把資料從使用者空間拷到核心緩衝區,程序2再從核心緩衝區把資料讀走,核心提供的這種機制稱為程序間通訊(IPC,InterProcess Communication).
1、管道(pipe)
管道是一種最基本的IPC機制,由pipe函式建立:
#include <unistd.h>
int pipe(int filedes[2]);
調⽤用pipe函式時在核心中開闢一塊緩衝區(稱為管道)⽤用於通訊,它有⼀一個讀端一個寫端,然後通過filedes引數傳出給使用者程式兩個檔案描述符,filedes[0]指向管道的讀端,filedes[1]指向管道的寫端(很好記,就像0是標準輸入1是標準輸出一樣)。所以管道在使用者程式看起來就像一個開啟的檔案,通過read(filedes[0]);或者write(filedes[1]);向這個檔案讀寫資料其實是在讀寫核心緩衝區。pipe函式呼叫成功返回0,呼叫失敗返回-1。 開闢了管道之後如何實現兩個程序間的通訊呢?比如可以按下面的步驟通訊:
在一個程序呼叫pipe函式建立管道,得到兩個檔案描述符指向讀端和寫段。
程序呼叫fork()建立子程序,那麼子程序會繼承父程序的檔案描述符,所以子程序也有兩個檔案描述符指向管道兩端。因為管道只能進行單向通訊,所以關閉父程序的讀端和子程序的寫端(也可以關閉父程序寫端和子程序讀端)。如下圖
這是建立管道進行通訊的過程,管道建立好之後再寫端寫入,在讀端就可以讀到寫入的資料。下面編寫程式碼進行測試。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<errno.h>
4 #include<string.h>
5
6 int main()
7 {
8 int _pipe[2];
9 int ret=pipe(_pipe); //建立管道
10 if(ret==-1)
11 {
12 printf("create pipe error!");
13 return 1;
14 }
15
16 pid_t id=fork(); //建立子程序
17 if(id<0 )
18 {
19 printf("fork error!");
20 return 2;
21 }
22 else if(id==0) //子程序
23 {
24 close(_pipe[0]); //關閉讀端
25 int i=0;
26 char *_mesg_c=NULL;
27 while(i<100)
28 {
29 _mesg_c="i am child";
30 write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
31 sleep(1);
32 i++;
33 }
34 }
35 else //父程序
36 {
37 close(_pipe[1]); //關閉寫端
38 char _mesg[100];
39 int j=0;
40 while(j<100)
41 {
42 memset(_mesg,'\0',sizeof(_mesg));
43 read(_pipe[0],_mesg,sizeof(_mesg));
44 printf("%s\n",_mesg);
45 j++;
46 }
47 }
48 return 0;
49 }
執行結果如圖:
其中“i am child”每隔一秒列印一次。
使⽤用管道需要注意以下4種特殊情況(假設都是阻塞I/O操作,沒有設定O_NONBLOCK標誌):
1. 如果所有指向管道寫端的檔案描述符都關閉了(管道寫端的引用計數等於0),而仍然有程序從管道的讀端讀資料,那麼管道中剩餘的資料都被讀後,再次read會返回0,就像讀到檔案末尾一樣。
2. 如果有指向管道寫端的檔案描述符沒關閉(管道寫端的引用計數大於0),而持有管道寫端的程序也沒有向管道中寫資料,這時有程序從管道讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會阻塞,直到管道中有資料可讀了才讀取資料並返回。
3. 如果所有指向管道讀端的⽂檔案描述符都關閉了(管道讀端的引用計數等於0),這時有程序向管道的寫端write,那麼該程序會收到訊號SIGPIPE,通常會導致程序異常終止。
4. 如果有指向管道讀端的檔案描述符沒關閉(管道讀端的引用計數大於0),而持有管道讀端的程序也沒有從管道中讀資料,這時有程序向管道寫端寫資料,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫⼊入資料並返回。