Linux Shell中管道的原理及C實現框架

在shell中咱們常常用到管道,有沒考慮過Shell是怎麼實現管道的呢?html

cat minicom.log | grep "error"

標準輸入、標準輸出與管道

咱們知道,每個進程都有3個標準的輸入輸出文件描述符shell

描述符編號 簡介 做用
0 標準輸入 通用於獲取輸入的文件描述符
1 標準輸出 通用輸出普通訊息的文件描述符
2 標準錯誤 通用輸出錯誤信息的文件描述符

咱們還知道,系統調用pipe能夠建立無名管道bash

int pipe(int pipefd[2]);

pipe的做用是建立無名管道,並建立兩個文件描述符code

文件描述符 做用
pipefd[0] 管道數據出口
pipefd[1] 管道數據入口

Shell實現管道的原理

在上文的基礎上,咱們再看看Shell如何實現管道的。htm

Shell中經過fork+exec建立子進程來執行命令。若是是含管道的Shell命令,則管道先後的命令分別由不一樣的進程執行,而後經過管道把兩個進程的標準輸入輸出鏈接起來,就實現了管道。blog

例如進程

grep "error" minicom.log | awk '{print $1}'

這句命名的做用很是簡單,ip

  1. 經過grep命令在minicom.log中檢索含有error關鍵字的行
  2. 經過awk命令打印grep的輸出結果中每一行的第一個字段

在Shell中要實現這樣的效果,有4個步驟:get

  1. 建立pipe
  2. fork兩個子進程執行grep和awk命令
  3. 把grep子進程的標準輸出、標準錯誤重定向到管道數據入口
  4. 把awk子進程的標準輸入重定向到管道數據出口

這樣就實現了Shell管道:grep把結果輸出到管道,awk從管道獲取數據cmd

Shell如何用C實現管道

我沒研究過Shell的代碼,但不妨礙咱們從功能倒推實現,若是是我,我會怎麼作呢?

int main(int argc, char **argv)
{
    while(1) {
        int pfds[2];
        pid_t cmd1, cmd2;
        if ((cmd1 = fork()) < 0) {
            ...
        } else if (cmd1 == 0) { /* child */
            /*
             * dup2 把 pfds[1](管道數據入口描述符) 複製到 文件描述符1&2
             * 實現把cmd1的標準輸出和標準錯誤 輸送到管道
             */
            dup2(pfds[1], STDOUT_FILENO);
            dup2(pfds[1], STDERR_FILENO);
            close(pfds[0]);
            close(pfds[1]);
            exec(cmd1...);
            __exit(127);
        }

        if ((cmd2 = fork()) < 0) {
            ...
        } else if (cmd2 == 0) { /* child */
            /*
             * dup2 把 pfds[0](管道數據出口描述符) 複製到 文件描述符0
             * 實現cmd2從管道中讀取(cmd1的輸出)數據
             */
            dup2(pfds[0], STDIN_FILENO);
            close(pfds[0]);
            close(pfds[1]);
            exec(cmd2...);
            __exit(127);
        }
        close(pfds[0]);
        close(pfds[1]);
        wait(...); 
    }
}
相關文章
相關標籤/搜索