前面咱們講了進程間通訊的一種方式,共享內存。下面看一看另外一種機制,匿名管道。
1.什麼是管道
管道是一個進程的數據流到另外一個進程的通道,即一個進程的數據輸出做爲另外一個進程的數據輸入,管道起到了橋樑的做用。
好比,在shell中輸入命令:ls -l|grep string,ls和grep是兩個進程,"|"符號表示管道,意思是執行ls -l進程,並將輸出結果result_1,做爲grep string進程的輸入result_0,grep進程將result_0中存在字符串string的信息打印到屏幕。html
2.管道的使用
1)popen函數:啓用一個新進程,並能夠向它傳遞數據,或者經過它接受數據。shell
FILE *popen(const char *command,conse char *open_mode);
command:運行的程序名和參數
open_mode:有兩個值"r(只讀)","w(只寫)"
"r":能夠獲取新進程的輸出
"w":能夠向新進程發送數據
返回值:返回輸入輸出文件流指針數組
2)pclose函數:關閉輸入輸出文件流指針
若調用該函數時,新進程仍然在運行,則pclose將等待,直至新進程結束。
返回值:返回新進程的退出碼。函數
3.popen函數使用示例
下例循環讀取read_fp輸出文件流的內容,寫入write_fp的輸入文件流,直到輸出流內容讀完。spa
#include<stdlib.h> #include<stdio.h> #include<string.h> int main() { FILE *read_fp = NULL; FILE *write_fp = NULL; char buffer[BUFSIZ+1]; int chars_read = 0; //初始化緩衝區 memset(buffer,'\0',sizeof(buffer)); read_fp = popen("ls -l","r"); write_fp = popen("grep rwxrwxr-x","w"); if(read_fp && write_fp) { chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp); while(chars_read) { buffer[chars_read]='\0'; //把數據寫入grep進程 fwrite(buffer,sizeof(char),chars_read,write_fp); chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp); } //關閉文件流 pclose(read_fp); pclose(write_fp); exit(EXIT_SUCCESS); } printf("%d\n",2); exit(EXIT_FAILURE); }
輸出結果:指針
三、popen的原理及優缺點
當調用popen運行一個新進程時,它首先啓動shell,而後將command參數傳遞給它。
優勢:可使用shell來分析命令字符串,啓動很是複雜的shell命令。
缺點:不只要啓動一個新進程,還要啓動一個shell,效率會比較低。code
4.pipe函數的使用htm
int pipe(int file_description[2]);
file_description[2]:表示管道的輸出輸入端,輸出端數據通過管道流到輸入端,函數執行完後, 會將這個數組賦值。
file_description[1]表示管道輸出端文件描述符
file_description[0]表示管道輸入端文件描述符
返回值:0成功,-1失敗blog
與popen不一樣的是,pipe函數是一個底層調用,不會啓動shell。
popen是使用文件流(FILE)工做的,pipe使用的是文件描述符,相應的數據要用底層的read和write來讀取和發送。進程
5.pipe函數使用示例
下例中,咱們在父進程中建立一個管道,而後調用fork建立一個子進程。
此時,父進程的file_description[1]輸出端,對應着子進程file_description[0]的輸入端。
數據經過管道由父進程傳到子進程。示例以下:
#include<stdlib.h> #include<stdio.h> #include<string.h> int main() { int data_processed = 0; const char data[]="Hello pipe!"; char buffer[BUFSIZ+1]; pid_t pid; memset(buffer,'\0',sizeof(buffer)); int filedes[2]; if(pipe(filedes)==0) { //建立管道成功 //fork子進程 pid=fork(); if(pid==-1) { fprintf(stderr,"Fork failure"); exit(EXIT_FAILURE); } if(pid==0) { data_processed = read(filedes[0],buffer,BUFSIZ); printf("read %d bytes:%s\n",data_processed,buffer); exit(EXIT_SUCCESS); } else { data_processed = write(filedes[1],data,strlen(data)); printf("wrote %d bytes:%s\n",data_processed,data); exit(EXIT_SUCCESS); } } exit(EXIT_FAILURE); }
輸出結果:
6.管道用做標準輸入和輸出
咱們知道標準的輸入描述符爲0,輸出描述符爲1,
爲了使用已經定義好的標準程序,如od命令,從標準輸入讀入數據。
須要將管道的輸入端描述符置爲0,此時,咱們須要用到一個輔助函數dup
dup函數:建立一個描述符,複製原有描述符參數的結構到新建的描述符。
int dup(int file_descriptor);
新的描述符規則是,使用最小的可用值。
要想使管道的輸入描述符爲標準輸入描述符,咱們能夠先關閉文件描述符0,而後調用dup,
此時新建的描述符即爲最小可用值0,標準輸入描述符。
close(0); dup(file_description[0]);
上例使用標準輸入描述符改造後的示例以下:
#include<stdlib.h> #include<stdio.h> #include<string.h> int main() { int data_processed = 0; const char data[]="Hello pipe!"; int filedes[2]; pid_t pid; if(pipe(filedes)==0) { pid = fork(); if(pid==-1) { fprintf("stderr","fork failure!\n"); exit(EXIT_FAILURE); } if(pid==0) { close(0); dup(filedes[0]); close(filedes[0]); close(filedes[1]); execlp("od","od","-c",0); exit(EXIT_FAILURE); } else { close(filedes[0]); data_processed = write(filedes[1],data,strlen(data)); close(filedes[1]); printf("wrote %d bytes:%s\n",data_processed,data); } } }
輸出結果:
7.匿名管道須要注意的問題
1)當管道沒有關閉時,若沒有數據可讀,read調用會阻塞
2)當管道關閉時,read調用會返回0
3)匿名管道通訊,進程間必須是父子關係。