fork()後,父子進程將有各自獨立的存儲空間,他們之間是沒法像同一進程之間經過傳參或使用全局變量來進行數據交換的。數組
怎麼辦?管道就是一種簡單的實現進程間通訊的方式。spa
管道被建立後,將獲得兩個文件描述符file_des[1]和file_des[0](描述符的名字取決於管道建立的參數)。code
file_des[1]用於向管道中寫入,file_des[0]用於從管道中讀。讀的順序和寫的順序一致。blog
這兩個文件描述符可被子進程繼承。因此,父子進程只要操做各自的這兩個文件描述符就能夠實現彼此通訊。繼承
一樣,一個進程建立了管道後,全部繼承於它的子進程、孫子進程之間均可以經過這個管道來進行通訊。進程
管道的建立方式:ip
經過系統調用pipe,頭文件unistd.h。string
int pipe (int file_des[2]); 建立失敗返回-1,成功返回0,並獲得兩個文件描述符file_des[1]和file_des[0]。it
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int main(){ int file[2]; //文件描述符數組 char buffer[BUFSIZ+1] const char data="hello"; int res = pipe(file); if(res == 0) //pipe調用成功,管道建立 { printf("pipe created!\n"); if(fork()==0){ read(file[0],buffer,BUFSIZ); //子進程從管道中讀 printf("read %s\n",buffer); exit(EXIT_SUCCESS); }else{ write(file[1],data,strlen(date)); //父進程向管道中寫 printf("write %s\n",data); } exit(EXIT_SUCCESS); }
那麼,將會獲得看到輸出:pip
write hello
read hello
也有可能上下兩行對調(與進程調度有關)。上述例子中fork()後,if 和 else裏的內容也可對調,變成父進程讀,子進程寫。
該圖清晰地說明了不一樣進程經過管道交換數據的方式。注意,父子進程分別有各自用於讀寫管道的文件描述符。
對於管道的write操做,若管道已滿,write會被阻塞,直到另外一頭有read將管道中數據取走;若全部read端都關閉了,write將返回-1,並設置error爲EPIPE。
對於管道的read操做:
1.若管道爲空,read將阻塞,直到另外一頭有write向管道寫;
2.若管道不爲空,若read調用計劃讀取的字節數a > 管道中的字節數b ,那麼就讀取b個字節,返回b;若a < b,則讀取a個字節,返回a。
3.管道的寫端已關閉(必須是引用這個管道的全部進程的file_des[1]均關閉),read將返回0。
管道應注意的問題:
當兩個進程同一時間段要實現雙向交換數據時,應該使用兩個管道,不然一個進程對同一個管道既讀又寫,很容易讀到本身寫進去的內容。
兩個進程同一時間段使用兩個管道進行雙向數據交換時,安排不當有可能產生死鎖問題:
例:父進程向管道A寫10個數據,子進程從A讀1個數據,讀到的數據經處理寫入管道B,父進程從B得到處理結果。
此時,若父進程向A寫入的某個數據過大,管道已滿,父進程write將阻塞,此時,子進程向B寫入的數據也很大,管道B也滿了,子進程write也阻塞,這時會陷入死鎖。