標籤:
13.4 pipe調用在看過進階的popen函數之後,再來看看底層的pipe函數.通過這個函數在兩個程式之間傳遞資料不需要啟動一個shell來解釋請求的命令.它同時提供了對讀寫資料的更多控制.
pipe函數的原型如下所示:
#include <unistd.h>int pipe(int file_descriptor[2]);
參數:是一個由兩個整數類型的檔案描述符組成的數組.
返回值:該函數在數組中填上兩個新的檔案描述符,如果成功則返回0,如果失敗則返回-1並設定errno來表明失敗的原因.
錯誤描述:
EMFILE:進程使用的檔案描述符過多
ENFILE:系統的檔案表已滿
EFAULT:檔案描述符無效
調用pipe之後,兩個檔案描述符以一種特殊的方式串連起來.寫到file_descrpter[1]的所有資料都可以從file_descripter[0]讀回.
資料基於先進先出的原則進行處理,這意味著如果把位元組1,2,3寫到file_descripter[1],從file_descripter[0]讀取到的資料也會是1,2,3.
特別要注意,這裡使用的是檔案描述符而不是檔案流,所以必須用底層的read和write調用來訪問資料,而不是檔案流庫函數fread和fwrite.
編寫程式pipe1.c,它用pipe函數建立一個管道.
<pre name="code" class="cpp">/************************************************************************* > File Name: pipe1.c > Description: pipe1.c程式用pipe函數建立一個管道 > Author: Liubingbing > Created Time: 2015年07月10日 星期五 10時54分58秒 > Other: pipe1.c程式用數組files_pipes[]的兩個檔案描述符建立一個管道. 然後用檔案描述符file_pipes[1]向管道中寫資料,用檔案描述符file_pipes[0]讀回資料 管道有一些內建的緩衝區,它在write和read調用之間儲存資料 ************************************************************************/#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>int main(){/* data_processed儲存傳回值write和read調用的傳回值 */int data_processed;/* 檔案描述符組成的數組,file_pipes[0]用於讀,file_pipes[1]用於寫 */int file_pipes[2];const char some_data[] = "123";char buffer[BUFSIZ + 1];memset(buffer, '\0', sizeof(buffer));/* pipe函數在file_pipes[0]和file_pipes[1]建立一個管道 */if (pipe(file_pipes) == 0) {/* write函數從指標some_data所指的記憶體中寫入strlen(some_data)個位元組到file_pipes[1]所指的檔案中 * 如果成功,則返回實際寫入的位元組數;如果失敗,則返回-1,並將錯誤碼儲存在errno中;此外返回0表示未寫入任何資料 */data_processed = write(file_pipes[1], some_data, strlen(some_data));printf("Wrote %d bytes\n", data_processed);/* read函數從檔案描述符file_pipes[0]指向的檔案中讀取BUFSIZ個位元組到buffer指向的記憶體中 * 如果成功,則返回實際讀取的位元組數;如果失敗,則返回-1,並將錯誤碼儲存在errno中;此外返回0表示未讀入任何資料,已經到達檔案尾 */data_processed = read(file_pipes[0], buffer, BUFSIZ);printf("Read %d bytes: %s\n", data_processed, buffer);exit(EXIT_SUCCESS);}exit(EXIT_FAILURE);}
程式運行結果如下所示:
這個程式用數組file_pipes[]的兩個檔案描述符建立一個管道.然後用file_pipes[1]向管道中寫資料,再用file_pipes[0]從管道讀回資料.
注意:管道有一些內建的緩衝區,它在write和read調用之間儲存資料
看起來,這個例子毫無用處,因為在這個程式內可以直接從some_data向buffer複製資料,完全不需要使用pipe建立管道.
管道的真正優勢在於,如果想在兩個進程傳遞資料的時候,當程式用fork調用建立新進程時,原先開啟的檔案描述符仍將保持開啟狀態.如果在原先的進程中建立一個管道,然後再調用fork建立新進程,即可通過管道在兩個進程之間傳遞資料.
兩個進程之間的通訊
編寫程式pipe2.c,在pipe1.c的基礎上使用fork調用,實現兩個進程之間的通訊
/************************************************************************* > File Name: pipe2.c > Description: pipe2.c程式在父進程中建立一個管道,然後調用fork建立子進程,通過管道在父進程和子進程之間傳遞資料 > Author: Liubingbing > Created Time: 2015年07月10日 星期五 11時41分09秒 > Other: pipe2.c程式 父進程----file_pipes[1](向管道寫資料)----file_pipes[0](從管道讀回資料)----子進程 ************************************************************************/#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>int main(){int data_processed;int file_pipes[2];const char some_data[] = "123";char buffer[BUFSIZ + 1];pid_t fork_result;memset(buffer, '\0', sizeof(buffer));/* pipe函數用file_pipes檔案描述符數組建立管道 * 用檔案描述符file_pipes[1]向管道中寫資料 * 用檔案描述符file_pipes[0]從管道中讀回資料 */if (pipe(file_pipes) == 0) {/* fork建立一個子進程 * 如果建立失敗,返回-1 * 如果成功,返回0表示子進程pid * 其他為父進程 */fork_result = fork();if (fork_result == -1) {fprintf(stderr, "Fork failure");exit(EXIT_FAILURE);}if (fork_result == 0) {/* 子進程中使用read系統調用從file_pipes[0]指向的檔案中讀取BUFSIZ個位元組的資料到buffer指向的記憶體 * 如果成功返回實際讀取資料的位元組數 *///sleep(2);data_processed = read(file_pipes[0], buffer, BUFSIZ);printf("Read %d bytes: %s\n", data_processed, buffer);exit(EXIT_SUCCESS);} else {/* 父進程中使用write系統調用從some_data指向的記憶體中讀入strlen(some_data)個位元組的資料到file_pipes[1]指向的檔案 * 如果成功返回實際讀入資料的位元組數 */data_processed = write(file_pipes[1], some_data, strlen(some_data));printf("Wrote %d bytes\n", data_processed);}}exit(EXIT_SUCCESS);}這個
程式首先用pipe調用建立一個管道,接著用fork調用建立一個新進程.如果fork調用成功,父進程就寫資料到管道中,而子進程從管道中讀取資料.父子進程都在只調用了一次write或read之後就退出.如果父進程在子進程之前退出,就會在兩部分輸出內容之間看到shell提示符.如下所示(./a.out在子進程中添加sleep(2)):
這樣
結合pipe和fork就可以在不同的進程之間進行讀寫資料.
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
linux程式設計——pipe調用在兩進程之間通訊(第十三章)