Linux管道(匿名PIPE),linux管道匿名pipe
管道基本概念
管道是Unix中最古老的處理序間通訊的形式。
我們把從一個進程串連到另一個進程的一個資料流稱為一個“管道”
如:ps aux | grep httpd | awk '{print $2}'
管道
管道的本質
固定大小的核心緩衝區
管道限制
1)管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;
2)匿名管道只能用於具有共同祖先的進程(如父進程與fork出的子進程)之間進行通訊;[通常,一個管道由一個進程建立,然後該進程調用fork,此後父子進程共用該管道]
匿名管道pipe
SYNOPSIS #include <unistd.h> int pipe(int pipefd[2]);
功能
建立無名管道
參數
Pipefd:檔案描述符數組,其中pipefd[0]表示讀端,pipefd[1]表示寫端
管道建立
//自己實現管道void err_exit(string str);int main(){ int pipefd[2]; if (pipe(pipefd) == -1) err_exit("pipe error"); pid_t pid; if ((pid = fork()) < 0) err_exit("fork error"); if (pid == 0) //In Child, Write pipe { close(pipefd[0]); //使得STDOUT_FILENO也指向pipefd[1],亦即ls命令的輸出將列印到管道中 dup2(pipefd[1],STDOUT_FILENO); //此時可以關閉管道寫端 close(pipefd[1]); execlp("/bin/ls","/bin/ls",NULL); //如果進程映像替換失敗,則列印下面出錯資訊 fprintf(stderr,"Child: execlp error"); exit(0); } //In Parent close(pipefd[1]); //使得STDIN_FILENO也指向pipefd[2],亦即wc命令將從管道中讀取輸入 dup2(pipefd[0],STDIN_FILENO); //此時可以關閉管道讀端 close(pipefd[0]); execlp("/usr/bin/wc","/usr/bin/wc","-w",NULL); //如果進程映像替換失敗,則列印下面出錯資訊 fprintf(stderr,"Parent: execlp error"); return 0;}void err_exit(string str){ perror(str.c_str()); exit(EXIT_FAILURE);}
樣本:管道編程實踐
void err_exit(string str);int main(){ int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child, Write pipe { close(pipefd[0]); //Close Read pipe string str("I Can Write Pipe from Child!"); write(pipefd[1],str.c_str(),str.size()); //Write to pipe close(pipefd[1]); exit(0); } //In Parent, Read pipe close(pipefd[1]); //Close Write pipe char buf[1024]; memset(buf,0,sizeof(buf)); read(pipefd[0],buf,sizeof(buf)); //Read from pipe cout << "Read from pipe: " << buf << endl; close(pipefd[0]); return 0;}void err_exit(string str){ perror(str.c_str()); exit(EXIT_FAILURE);}
匿名管道讀寫規則
1)管道空時
O_NONBLOCK disable:read調用阻塞,即進程暫停執行,一直等到有資料來到為止。
O_NONBLOCK enable:read調用返回-1,errno值為EAGAIN。
2)管道滿時
O_NONBLOCK disable: write調用阻塞,直到有進程讀走資料
O_NONBLOCK enable:調用返回-1,errno值為EAGAIN
3)管道不停被寫,寫滿
O_NONBLOCK disable: write調用阻塞(Block)
O_NONBLOCK enable:調用返回-1,errno值為EAGAIN
//樣本:設定父進程Unblock讀PIPEint main(){ int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child, Write pipe { sleep(10); close(pipefd[0]); //Close Read pipe string str("I Can Write Pipe from Child!"); write(pipefd[1],str.c_str(),str.size()); //Write to pipe close(pipefd[1]); exit(0); } //In Parent, Read pipe close(pipefd[1]); //Close Write pipe char buf[1024]; memset(buf,0,sizeof(buf)); //Set Read pipefd UnBlock! int flags = fcntl(pipefd[0],F_GETFL); flags |= O_NONBLOCK; ret = fcntl(pipefd[0],F_SETFL,flags); if (ret != 0) { err_exit("Set UnBlock error"); } int readCount = read(pipefd[0],buf,sizeof(buf)); //Read from pipe if (readCount < 0) { //read立刻返回,不再等待子進程發送資料 err_exit("read error"); } cout << "Read from pipe: " << buf << endl; close(pipefd[0]); return 0;}
4)如果所有管道寫端對應的檔案描述符被關閉,則read返回0
int main(){ int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child { //close all close(pipefd[0]); close(pipefd[1]); exit(0); } //In Parent sleep(1); close(pipefd[1]); //Close Write pipe, Now all pipefd[1] Closed!!! char buf[1024]; memset(buf,0,sizeof(buf)); int readCount = read(pipefd[0],buf,sizeof(buf)); //Read from pipe if (readCount == 0) { cout << "OK, read 0 byte" << endl; } close(pipefd[0]); return 0;}
5)如果所有管道讀端對應的檔案描述符被關閉,則write操作會產生訊號SIGPIPE
void onSignalAction(int signalNumber){ switch(signalNumber) { case SIGPIPE: cout << "receive signal SIGPIPE: " << signalNumber << endl; break; default: cout << "other signal" << endl; break; }}int main(){ if (signal(SIGPIPE,onSignalAction) != 0) { err_exit("signal error"); } int pipefd[2]; int ret; if ((ret = pipe(pipefd)) != 0) { err_exit("pipe error"); } pid_t pid = fork(); if (pid == -1) { err_exit("fork error"); } if (pid == 0) //In Child, Write pipe { //Wait Parent Close pipefd[0] sleep(1); close(pipefd[0]); string str("I Can Write Pipe from Child!"); write(pipefd[1],str.c_str(),str.size()); //Write to pipe close(pipefd[1]); exit(0); } //In Parent, Close All Pipe close(pipefd[1]); close(pipefd[0]); wait(NULL); return 0;}
Linux PIPE特徵
1)當要寫入的資料量不大於PIPE_BUF時,Linux將保證寫入的原子性。
2)當要寫入的資料量大於PIPE_BUF時,Linux將不再保證寫入的原子性。
//樣本:測試PIPE_BUF大小int main(){ int pipefd[2]; int ret = pipe(pipefd); if (ret < 0) { err_exit("pipe error"); } int flags = fcntl(pipefd[1],F_GETFL); flags |= O_NONBLOCK; ret = fcntl(pipefd[1],F_SETFL,flags); if (ret < 0) { err_exit("fcntl error"); } //Write test unsigned int countForTestPipe = 0; while (true) { ret = write(pipefd[1],"a",1); if (ret < 0) { break; } ++ countForTestPipe; } cout << "size = " << countForTestPipe << endl;}/**測試結果:Ubuntu 14.04 X64 xiaofang@xiaofang-Lenovo-G470:~/apue/it$ ./main size = 65536*/
附-管道容量查詢
man 7 pipe
附-深入理解檔案描述符
int main(){ close(STDIN_FILENO); if (open("readfile.txt",O_RDONLY) == -1) { err_exit("open read error"); } close(STDOUT_FILENO); if (open("writefile.txt",O_WRONLY|O_TRUNC|O_CREAT,0644) == -1) { err_exit("open write error"); } if (execlp("/bin/cat","/bin/cat",NULL) == -1) { err_exit("execlp error"); } return 0;}