建立 shell 樣式的管道

shell 樣式的管道由兩個或多個程序組成,每個程序的標準輸出連線到下一個的標準輸入。輸出到輸入的連線建立在管道上。為了建立類似管道的程序集,可以建立另一個示例中描述的管道連線子程序,並且還使用 dup2(2)函式將每個管道端複製到其程序的相應標準檔案描述符上。通常最好關閉原始的管道末端檔案描述符,特別是如果通常情況下,一個人打算讓孩子執行不同的命令。

// Most error handling omitted for brevity, including ensuring that pipe ends are
// always closed and child processes are always collected as needed; see other
// examples for more detail.
int demo() {
    int pipefds[2];
    pid_t child1 = -1, child2 = -1;

    pipe(pipefds);

    switch (child1 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 1 */
            close(pipefds[0]);
            dup2(pipefds[1], STDOUT_FILENO);
            close(pipefds[1]);
            execl("/bin/cat", "cat", "/etc/motd", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    switch (child2 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 2 */
            close(pipefds[1]);
            dup2(pipefds[0], STDIN_FILENO);
            close(pipefds[0]);
            execl("/bin/grep", "grep", "[Ww]ombat", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    // Only the parent reaches this point
    close(pipefds[0]);
    close(pipefds[1]);
    wait(NULL);
    wait(NULL);
}

設定管道的一個常見錯誤是忘記讓一個或多個程序關閉它未使用的管道端。根據經驗,最終結果應該是每個管道端都由一個程序擁有,並且只在該程序中開啟。

在上面的演示函式的情況下,第二個孩子或父母關閉 pipefds[1] 失敗將導致第二個孩子掛起,因為 grep 將繼續等待輸入,直到它看到 EOF,並且不會被觀察到因為管道的寫入端在任何程序中都保持開啟狀態,例如父程序或第二個子程序本身。