管道和重新導向
輸入輸出不僅可以重新導向到檔案,也可以通過管道傳遞到其他進程中。
基本思路:
linux:
1. 建立管道對
pipe(int pipefd[2])
2. 建立進程
首先fork,然後子進程用dup2把pipefd[0]複製到STDIN_FILENO(就是0),然後關閉pipefd[1],或者把pipefd[1]複製到STDOUT_FILENO(就是1),然後關閉pipefd[0]。
之後調用exec去載入要建立的進程。
3. 讀或寫
用read或者write。注意讀寫之前要把子進程使用的那一個用close函數關閉掉。
區別在*nix建立進程分兩步,fork然後exec。windows只一步。
區別2,*nix要手工dup來複製檔案描述符,windows在建立進程的API內部做好了這件事。
linux步驟多一些,但是靈活,可以變化出多種模式來,例如fork之後,不僅僅子進程可以exec,父進程也可以exec,完完全全實現像“ls|wc -l”這樣的功能。也就是子進程用wc去替代,父進程用ls去替代。
windows步驟少,模式固定,它要實現上述功能,反而更加繁瑣,需要建立兩個進程,並把它們對接,然後等待兩個進程結束
執行個體:
題目:通過管道類比shell命令:catfile | sort
涉及主要知識點:未具名管道、重新導向
題目描述:具體類比一個shell命令:cat file | sort。具體的一些提示在後面。
提示:首先在目前的目錄下建立一個名為”file”的檔案,裡面的內容輸入如下:
99
123
892
12
1342
89
32
76
通過執行”cat file | sort”後的結果如下:
12
123
1342
32
76
89
892
99
現在我們需要通過一個管道,將”cat file”的結果通過管道送給命令”sort”。其中,將”cat file”命令的輸出結果重新導向到”sort”命令的輸入是由包含在命令中的管道標誌”|”來完成的。為了在程式當中實作類別似的功能,需要用dup()或者dup2()系統調用將標準輸入和標準輸出聯絡起來,具體的系統調用使用可以通過man手冊來查,或者查閱群共用裡面的函數手冊。另外,這裡的”cat file”和”sort”命令不是自己來完成,而是通過調用exec函數族來實現的。
******************************************************************************************************************************************
******************************************************************************************************************************************
我的原始碼如下:
1 #include <stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<unistd.h> 5 #include<sys/types.h> 6 #include<sys/stat.h> 7 8 int main(intarg,char *argv[],char **environ) 9 { 10 int fd[2]; 11 pid_t pid; 12 int stat_val; 13 14 if(arg < 2) 15 { 16 printf("wrong parameters\n"); 17 exit(0); 18 } 19 if(pipe(fd)) 20 { 21 printf("Create pipefailed!\n"); 22 exit(1); 23 } 24 25 pid=fork(); 26 switch(pid) 27 { 28 case -1: 29 perror("forkfailed!\n"); 30 exit(1); 31 case 0: 32 //close(0); 33 //dup(fd[0]); 34 dup2(fd[1],1); 35 close(fd[0]); 36 execlp("cat","cat","file",NULL); 37 exit(0); 38 default: 39 dup2(fd[0],0); 40 close(fd[1]); 41 execlp("sort","sort",NULL); 42 exit(0); 43 } 44 wait(&stat_val); 45 exit(0); 46 return 0; 47 }************************************************************************************************************************************************************************************************************************************************************************************編譯sort.c:jingjing@jingjing-laptop:~/公用的$ gccsort.c -o sort 運行程式:jingjing@jingjing-laptop:~/公用的$ ./sortcat file|sort12123134232768989299*****
流程圖:
1』pipe()建立管道、fork()建立子進程
這裡,0-1-2分別表示標準輸入,標準輸出,標準錯誤。先建立管道,再由fork建立子進程,這裡子進程是完全繼承父進程的檔案描述符表。 『2』父進程和子進程分別執行dup2()
重新導向以後,父進程的標準輸入來自管道,子進程的標準輸出到管道。也就是將父進程的檔案描述符[3]處的指標拷貝到[0]對應的條目處;將子進程的檔案描述符4處的指標拷貝到[1]對應的條目處。 『3』各自調用close以後
調用close()關閉檔案描述符,相當於刪除檔案描述符表的對應條目。這裡,
close(fd[0])和close(fd[1])的操作是不可以省略的:執行了2個 close以後,參見圖4,
只有子進程的ls
-l才會像管道寫資料,最終子進程終止,父進程會檢測到檔案結束;否則如果省略到close(),按照圖3所示,子進程終止以後,父進程的檔案描述符[4]仍然是為管道的寫操作開啟著,父進程將陷入無限期的阻塞,等待資料的寫入。
注意: 關於這裡 在父、子進程中都需要 close() 兩次 有待驗證!
心得:
1
子進程的功能是讀出資訊,而父進程的功能是寫入資訊;因此在子進程那裡,要把管道的輸入端複製到標準輸出中,即dup2(fd[1],1)而父進程則要把管道寫端複製到標準輸入,即dup2(fd[0],0)。
2
dup()與dup2()的最大區別:
舉例說明:
close(1); //關閉標準輸出
dup(fd[1]); //把管道的輸入端複製到標準輸出
這兩個語句等價於: dup2(fd[1],1);
所以dup2()是把close()和dup()這兩個函數整合在了同一個函數裡。這裡特別要注意的是:dup2()的兩個參數,是把前一個複製到後一個裡面,所以書上的例子中寫的是錯誤的。
3、還有就是exec()函數的用法,我一開始不知道“cat file“命令如何作為函數的參數,現在談談我的理解吧:
我的程式中execlp("cat","cat","file",NULL);第一個參數是為了指明該命令的路徑,第二個和第三個參數是該命令
所包含的所有參數,即把他們一一羅列出來。