一,什麼是管道
管道是Linux支援的最初Unix IPC形式之一,具有以下特點:
- 管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;
- 只能用於父子程式或者兄弟程式之間(具有親緣關係的程式);
- 單獨構成一種獨立的檔案系統:管道對於管道兩端的程式而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,而是自立門戶,單獨構成一種檔案系統,並且只存在與記憶體中。
- 資料的讀出和寫入:一個程式向管道中寫的內容被管道另一端的程式讀出。寫入的內容每次都新增在管道緩衝區的末尾,並且每次都是從緩衝區的頭部讀出資料。
管道的實現機制:
管道是由核心管理的一個緩衝區,相當於我們放入記憶體中的一個紙條。管道的一端連線一個程式的輸出。這個程式會向管道中放入資訊。管道的另一端連線一個程式的輸入,這個程式取出被放入管道的資訊。一個緩衝區不需要很大,它被設計成為環形的資料結構,以便管道可以被迴圈利用。當管道中沒有資訊的話,從管道中讀取的程式會等待,直到另一端的程式放入資訊。當管道被放滿資訊的時候,嘗試放入資訊的程式會等待,直到另一端的程式取出資訊。當兩個程式都終結的時候,管道也自動消失。
二,管道的建立(pipe)
包含標頭檔案<unistd.h>
功能:建立一無名管道
原型:
int pipe(int fd[2]);
引數:
fd:檔案描述符陣列,其中fd[0]表示讀端, fd[1]表示寫端
返回值:成功返回0,失敗返回錯誤程式碼
man幫助說明:
DESCRIPTION
pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write end of the pipe is buffered by the kernel until it is read from the read end of the pipe. For further details, see pipe(7).
該函式建立的管道的兩端處於一個程式中間,在實際應 用中沒有太大意義,因此,一個程式在由pipe()建立管道後,一般再fork一個子程式,然後通過管道實現父子程式間的通訊(因此也不難推出,只要兩個 程式中存在親緣關係,這裡的親緣關係指的是具有共同的祖先,都可以採用管道方式來進行通訊)。父子程式間具有相同的檔案描述符,且指向同一個管道pipe,其他沒有關係的程式不能獲得pipe()產生的兩個檔案描述符,也就不能利用同一個管道進行通訊。
建立管道後示意圖:
三,利用管道進行父子程式間資料傳輸
示例一:子程式向管道中寫資料,父程式從管道中讀出資料
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fds[2]; if(pipe(fds) == -1){ perror("pipe error"); exit(EXIT_FAILURE); } pid_t pid; pid = fork(); if(pid == -1){ perror("fork error"); exit(EXIT_FAILURE); } if(pid == 0){ close(fds[0]);//子程式關閉讀端 write(fds[1],"hello",5); exit(EXIT_SUCCESS); } close(fds[1]);//父程式關閉寫端 char buf[10] = {0}; read(fds[0],buf,10); printf("receive datas = %s\n",buf); return 0; }
結果:
示例二:利用管道實現ls |wc –w功能
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> int main(void) { int fds[2]; if(pipe(fds) == -1){ perror("pipe error"); exit(EXIT_FAILURE); } pid_t pid; pid = fork(); if(pid == -1){ perror("fork error"); exit(EXIT_FAILURE); } if(pid == 0){ dup2(fds[1],STDOUT_FILENO);//複製檔案描述符且指定新複製的fd為標準輸出 close(fds[0]);//子程式關閉讀端 close(fds[1]); execlp("ls","ls",NULL); fprintf(stderr,"exec error\n"); exit(EXIT_FAILURE); } dup2(fds[0],STDIN_FILENO); close(fds[1]);//父程式關閉寫端 close(fds[0]); execlp("wc","wc","-w",NULL); fprintf(stderr, "error execute wc\n"); exit(EXIT_FAILURE); }
結果: