1 管道(Pipe)
管道是UNIX系統IPC的最古老的形式,並且所有的Unix系統都提供這種通訊機制,當然也包括Linux。這樣利用管道進行IPC管道具有如下限制:
1、曆史原因造成管道是半雙工的,資料只能單向流動。如果想雙向通訊,必須要建立兩個管道。
2、管道通訊雙方必須有親緣關係的進程之間(父子進程或者兄弟進程之間)。
2 管道的建立
#include <unistd.h>int pipe(int pipefd[2]);
經由pipefd參數返回兩個檔案描述符,pipefd[0]描述符用來讀取管道中的資料,可以簡稱為管道的讀端;pipefd[1]檔案描述符用來向管道寫入資料,簡稱為管道的寫端。
返回的pipefd描述符都位於同一個進程中,沒有任何意義。通常調用pipe的進程緊接著調用fork,這樣就可以建立從父進程到子進程的管道(父進程
關閉pipefd[0],子進程關閉pipefd[1])或者從子進程到父進程的通訊(父進程關閉pipefd[1],子進程關閉pipefd[0]),
用管道進行處理序間通訊了。
3 管道的讀寫
管道的讀寫是通過系統調用read和write完成的。pipefd[0]和pipefd[1]分別對應管道的讀端和寫端,pipefd[0]描述符用來
讀取管道中的資料,pipefd[1]檔案描述符用來向管道寫入資料。如果向pipefd[0]寫資料,或者向pipefd[1]讀資料都將會得到錯誤。
當管道一端關閉之後,遵循如下規則:
1、當所有的寫端都關閉時,當管道中所有的資料都被讀取後,read將會返回0,以指示達到了檔案末尾。理論上如果還有進程沒有關閉管道的寫端的話,讀端將不會到達檔案末尾。
2、如果管道的讀端已經關閉,再向管道寫資料的話,將會產生SIGPIPE訊號。預設SIGPIPE訊號處理是結束當前進程,如果當前進程忽略SIGPIPE訊號,則write函數返回-1,errno置為EPIPE。
系統常量PIPE_BUF定義了管道的大小。該系統常量定義在limits.h中,運行期可以通過pathconf或者fpathconf系統調用查詢
PIPE_BUF的值。Linux中PIPE_BUF的值為4096,假如你在編寫可移植的程式,請用PIPE_BUF這個宏,而不要用4096這個數值
(不同Unix系統上面PIPE_BUF值的大小是不一樣的)。
對管道寫入資料小於等於PIPE_BUF時,系統保證這個寫入操作是原子操作,write將會一次性寫入管道,並返回,當系統多個進程寫管道時,將保證不
會穿插寫入。如果寫入資料量大於PIPE_BUF,則系統將不再保證寫入操作為原子操作,當管道有空間,write就將會寫入一部分資料,當所有的資料都
寫入管道之後,write返回,當有多個進程同時寫管道時,將會出現穿插寫入的情況。
4 管道應用執行個體
#include <stdio.h>#include <unistd.h>#include <errno.h>#include <stdlib.h>int main(void){int n;int fd[2];pid_t pid;char line[1024];if(pipe(fd) < 0){fprintf(stderr, "pipe error: %s\n", strerror(errno));exit(-1);}if((pid = fork()) < 0){fprintf(stderr, "fork error: %s\n", strerror(errno));exit(-1);}else if(pid > 0) /* Parent */{close(fd[1]);n = read(fd[0], line, 1024);write(STDOUT_FILENO, line, n);}else /* Child */{close(fd[0]);write(fd[1], "Hello World\n", 12);}}
通過例子我們可以看到,管道資料流向是從子進程到父進程的。父進程關閉了管道的寫端,保留管道的讀端,而子進程則關閉了管道的讀端,而保留管道的寫端。
5 管道的總結
通過上面的描述,我們可以總結一下他的特點:
1、資料單向流動。
2、沒有管道命名,所以只能在有親緣關係的進程間傳遞資料。
3、管道的大小為PIPE_BUF,寫入資料不大於這個值則系統保證為原子操作,否則不保證為原子操作。
4、管道所傳遞的為無格式位元組流。所以需要管道兩端的進程之間事先定義好傳輸協議。
參考資料:
UNIX環境進階編程(AUPE)
Linux環境處理序間通訊(一)
原文