Linux網絡編程學習(六) ----- 管道(第四章)
1、管道的定義
管道就是將一個程序的輸出和另外一個程序的輸入連接起來的單向通道,比如命令: ls -l|more,就建立了一個管道,獲取ls -l的輸出作為more的輸入,數據就沿著管道從管道的左邊流到了管道的右邊。
實際上內核為進程建立了兩個句柄f1和f2,進程通過句柄f1向管道寫入數據,同時通過f2從管道讀出數據,這裏是同一個進程建立了自己的管道
進程間的管道:通過fork出來的子進程,與父進程一樣,擁有對同一個管道的讀寫句柄,但管道是單向的,因此要決定數據流向,如父進程到子進程或者子進程到父進程,那麽不需要的句柄就要關閉
2、管道的建立和使用
1)通過pipe()函數建立管道,函數聲明是int pipe(fd[2]) ,其中fd[0]用來從管道讀,fd[1]向管道寫
#include <stdio.h> #include <unistd.h> #include <sys/types.h> int main(void) { int fd[2], nbytes; pid_t childpid; char string[] = “Hello, world!\n”; char readbuffer[80]; pipe(fd); if((childpid = fork()) == -1) { perror(“fork”); exit(1); } if(childpid == 0) { /* 子進程關閉管道的讀句柄 */ close(fd[0]); /* 通過寫句柄向管道寫入信息 */ write(fd[1], string, strlen(string)); _exit(0); } else { /* 父進程關閉管道的寫句柄 */ close(fd[1]); /* 通過讀句柄從管道讀出信息 */ nbytes = read(fd[0], readbuffer, sizeof(readbuffer)); printf(“Received string: %s”, readbuffer); } return(0); }
假定了數據流向是子進程到父進程,那麽就由子進程寫,父進程讀,所以子進程要關閉讀句柄,而父進程要關閉寫句柄
2)使用popen()/pclose()函數,函數聲明是FILE* popen(char * command, char *type)
popen()首先調用pipe()建立一個管道,然後用fork()函數建立一個子進程,運行一個shell環節,然後在這個環境中運行"command"參數指定的程序,數據管道流向由type控制,要麽為"r"或者為"w",兩者取其一,且type只取一個字符,比如"rw",那麽只取"r",最後用pclose()關閉即可
#include <stdio.h> #define MAXSTRS 5 int main(void) { int cntr; FILE *pipe_fp; char *strings[MAXSTRS] = { "roy", "zixia", "gouki","supper", "mmwan"}; /* 用popen 建立管道 */ if (( pipe_fp = popen("sort", "w")) == NULL) { perror("popen"); exit(1); } /* Processing loop */ for(cntr=0; cntr<MAXSTRS; cntr++) { fputs(strings[cntr], pipe_fp); fputc(‘\n‘, pipe_fp); } /* 關閉管道 */ pclose(pipe_fp); return(0); }
popen()可以節省源代碼,同時還可以在"command"中使用任意合法的shell指令,包括重定向和管道,如下均為合法
popen("ls ~roy", "r"); popen("sort > /tmp/zixia", "w"); popen("sort | uniq | more", "w");
如下例子為建立了兩個管道,一個讀管道,一個寫管道
#include <stdio.h> int main(void) { FILE *pipein_fp, *pipeout_fp; char readbuf[80]; /* 用popen 建立一個通向"ls:的讀管道*/ if (( pipein_fp = popen("ls", "r")) == NULL) { perror("popen"); exit(1); } /* 用popen 建立一個通向"sort"的寫管道 */ if (( pipeout_fp = popen("sort", "w")) == NULL) { perror("popen"); exit(1); } /* 進程循環 */ while(fgets(readbuf, 80, pipein_fp)) { fputs(readbuf, pipeout_fp); } /* 關閉打開的管道 */ pclose(pipein_fp); pclose(pipeout_fp); return(0); }
3、tips
1)pipe()調用必須在fork()前
2)及時關閉不需要的管道句柄
3)管道只能實現父子進程之間的通信,若兩個進程沒有fork()關系,必須考慮其他進程通信方式
Linux網絡編程學習(六) ----- 管道(第四章)