對shell有必定了解的人都知道,管道和重定向是 Linux 中很是實用的 IPC 機制。shell
在shell中,咱們一般使用符合‘|’來表示管道,符號‘>’和‘<’表示重定向。數組
那麼管道和重定向的真實含義(定義)又是什麼呢?ide
管道就是一個進程與另外一個進程之間通訊的通道,它一般是用做把一個進程的輸出經過管道鏈接到另外一個進程的輸入。函數
它是半雙工運做的,想要同時雙向傳輸須要使用兩個管道。3d
管道又能夠分爲匿名管道和命名管道,而shell中使用到的是匿名管道,因此本文僅描述匿名管道。code
例如命令ls | grep main.c,使用了管道來鏈接了兩條命令來執行,可以快速地讓咱們知道當前目錄下是否有 main.c 文件。blog
管道的本質是內存中的緩衝區,能夠看做是打開到內存中的文件。繼承
因此須要使用兩個文件描述符來索引它,一個表示讀端,一個表示寫端。索引
而且規定,數據只能從讀端讀取、只能往寫端寫入。進程
使用函數pipe()能夠建立匿名管道,須要包含頭文件 unistd.h,示例代碼:
int fd[2]; pipe(fd);
首先建立一個 2 個元素的整型數組,而後將該數組做爲pipe()的參數,pipe()執行成功後,數組元素 fd[0]的值就會變成所建立的管道的讀端的文件描述符,fd[1]就會變成寫端的文件描述符。
至此管道就算建立成功了。
管道建立成功後,就能夠直接使用 read()和 write()函數對管道進行數據的讀寫。
而由於shell中都是使用標準輸入輸出對管道進行讀寫的,例如ls | grep main.c就是將 ls 的標準輸出寫到了管道寫端,而 grep 的標準輸入則從管道讀端讀取,因此本文也只描述此方法。
示例代碼以下:
int fd[2]; pipe(fd); pid=fork(); if(0==pid) //execute next command in child process { dup2(fd[0],0);//redirect standard input to pipe(read) close(fd[0]); close(fd[1]); if(0!=execvp(cmd0[0],cmd0)) printf("No such command!\n"); exit(EXIT_SUCCESS); } else //execute current command in current process { dup2(fd[1],1);//redirect standard output to pipe(write) close(fd[0]); close(fd[1]); if(0!=execvp(cmd1[0],cmd1)) printf("No such command!\n"); exit(EXIT_SUCCESS); }
首先是建立一個管道,而後建立子進程,子進程會繼承這一個管道,也就保證了父進程與子進程操做的是同一個管道(管道的繼承與普通變量不一樣)。
若是咱們但願在子進程中執行管道的讀端的程序例如ls | grep main.c中的grep main.c;在父進程中執行管道的寫端的程序,例如ls | grep main.c中的ls。
文件描述符,本質是非負整數,一般是小整數;它是一個索引,經過該索引能夠找到對應的文件。
例如,標準輸入、標準輸出、標準錯誤的文件描述符默認是 0、一、2 。當進程須要從標準輸入中讀取數據時,就會經過 0 索引找到標準輸入所對應的內存緩衝區來讀取數據。
假設此時管道讀端的文件描述符爲 三、寫端文件描述符爲 4 。
調用dup2(fd[0],0),實際上就是將文件描述符 3 指向的文件表項賦值給了文件描述符 0,而文件描述符 0 正是進程默認的標準輸入。
因此此時,當進程須要從標準輸入讀取數據時,進程就會經過文件描述符 0 來找到管道讀端所對應內存緩衝區。
從而實現了經過標準輸入來讀取管道的數據,也能夠說是,將管道的讀端重定向到了標準輸入。管道的寫端與標準輸入的關係也與此相似,此處再也不贅述。
調用dup2(fd[0],0)以後還須要調用close()函數將管道原有的文件描述符關閉,關閉的意思是文件描述符 3 和 4 再也不索引到管道或者其餘文件,也就是說此時使用 read 函數從文件描述符 3 中是讀取不到管道的數據的了,並非說關閉管道的意思。
須要注意的是,調用 exec 族函數並不會把管道這種 IPC 資源覆蓋或者從新初始化。
文件重定向
其實與上面管道重定向到標準輸入輸出很相似,甚至能夠直接採用上面所說的方法來實現。可是此處將講述一種更加簡潔的方法實現。
實例代碼以下:
char fileName[20]="out.txt"; freopen(fileName,"w",stdout);//redirect stdout to fileName
以上兩行簡單的代碼就實現了,將該進程的標準輸出重定向到了文件 out.txt ,甚至一行就能夠實現。
執行以上代碼後,當前進程的全部標準輸出,也就是 printf()之類的輸出全都會被寫到文件 out.txt,顯示屏將不會有輸出。
而將進程的標準輸入重定向到文件 in.txt 的代碼以下:
char fileName[20]="in.txt"; freopen(fileName,"r",stdin);//redirect stdin to fileName
其中的核心函數就是freopen():