使用 Fork 和 Dup 的 Unix 管道

问题描述 投票:0回答:2

假设在一个程序中我想执行两个进程,一个进程执行

ls -al
命令,然后将结果通过管道传输到
wc
命令,并在终端上显示输出。如何使用管道文件描述符来做到这一点?到目前为止我写的代码:

int main(int argc, char* argv[]) {
    int pipefd[2];
    int pipefd2[2];

    pipe(pipefd2);
    if ((fork()) == 0) {
       dup2(pipefd2[1], STDOUT_FILENO);
       close(pipefd2[0]);
       close(pipefd2[1]);
       execl("ls", "ls", "-al", NULL);
       exit(EXIT_FAILURE);
    } 

    if ((fork()) == 0){
        dup2(pipefd2[0], STDIN_FILENO);
        close(pipefd2[0]);
        close(pipefd2[1]);
        execl("/usr/bin/wc", "wc", NULL);
        exit(EXIT_FAILURE);
    }
    close(pipefd[0]);
    close(pipefd[1]);
    close(pipefd2[0]);
    close(pipefd2[1]);
}

一个例子会很有帮助。

c process pipe unix
2个回答
11
投票

您的示例代码在语法和语义上都被破坏了(例如,pipefd2 没有被声明,pipefd 和 pipelinefd2 之间的混淆等)因为这听起来像家庭作业,请确保您理解我下面的注释,并在需要时询问更多信息。我省略了对 pipeline、fork 和 dup 的错误检查,但理想情况下它们应该存在。

int main(int argc, char *argv[]) {
    int pipefd[2];
    pid_t ls_pid, wc_pid;

    pipe(pipefd);

    // this child is generating output to the pipe
    //
    if ((ls_pid = fork()) == 0) {
        // attach stdout to the left side of pipe
        // and inherit stdin and stdout from parent
        dup2(pipefd[1],STDOUT_FILENO);
        close(pipefd[0]);              // not using the right side

        execl("/bin/ls", "ls","-al", NULL);
        perror("exec ls failed");
        exit(EXIT_FAILURE);
    } 

    // this child is consuming input from the pipe
    //
    if ((wc_pid = fork()) == 0) {
        // attach stdin to the right side of pipe
        // and inherit stdout and stderr from parent
        dup2(pipefd[0], STDIN_FILENO);

        close(pipefd[1]);              // not using the left side
        execl("/usr/bin/wc", "wc", NULL);
        perror("exec wc failed");
        exit(EXIT_FAILURE);
    }

    // explicitly not waiting for ls_pid here
    // wc_pid isn't even my child, it belongs to ls_pid

    return EXIT_SUCCESS;
}

0
投票

代码

我已经尽可能彻底地注释掉了代码。我还添加了一些小错误处理来处理进程分叉和命令执行。正如 @msw 所指出的,您只需要一个管道即可在两个进程之间进行单向通信。

// ls-wc-pipe.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    int pipefd[2];
    pid_t ls_pid, wc_pid;

    pipe(pipefd);

    // CHILD PROCESS: ls
    if ((ls_pid = fork()) == 0) {
        // We close-and-then-connect STDOUT to the pipe's write end so the process
        // write to the pipe instead of the screen/terminal.
        dup2(pipefd[1], STDOUT_FILENO);

        // Since STDOUT_FILENO also refers to the pipe's write end, we can
        // close this file descriptor; it's no longer needed.
        close(pipefd[1]);

        // This process doesn't read from the pipe's read end so this is unused
        // and we should close it.
        close(pipefd[0]);

        // Execute the `ls` command, and exit if errors out.
        if ((execl("/bin/ls", "ls", "-al", (char *) NULL)) < 0) exit(0);
    }
    else if (ls_pid < 0) {
        fprintf(stderr, "failed to fork ls process");
        exit(0);
    }

    // CHILD PROCESS: wc
    if ((wc_pid = fork()) == 0) {
        // We close-and-then-connect STDIN to the pipe's read end so the process
        // read from the pipe instead of from the keyboard.
        dup2(pipefd[0], STDIN_FILENO);

        // Since STDIN_FILENO also refers to the pipe's read, we can
        // close this file descriptor; it's no longer needed.
        close(pipefd[0]);

        // This process doesn't write to the pipe's write end so this is unused
        // and we should close it.
        close(pipefd[1]);

        // Execute the `wc` command, and exit if errors out.
        if ((execl("/usr/bin/wc", "wc", (char *) NULL)) < 0) exit(0);
    }
    else if (wc_pid < 0) {
        fprintf(stderr, "failed to fork wc process");
        exit(0);
    }
    
    // PARENT PROCESS

    // The parent process isn't using the pipe, however these descriptors are
    // another references to the pipe's read and write ends and we must close
    // them. Otherwise, it doesn't send the EOF so the children can continue
    // (children block until all input has been processed).
    close(pipefd[0]);
    close(pipefd[1]);

    // The parent process waits for both children to finish before exiting.
    int ls_status, wc_status;
    pid_t ls_wpid = waitpid(ls_pid, &ls_status, 0);
    pid_t wc_wpid = waitpid(wc_pid, &wc_status, 0);

    // Return main's status based on whether the parent process waited both
    // child processes successfully. Status based only on `ls_status`.
    return
        ls_pid == ls_wpid && WIFEXITED(ls_status) &&
        wc_pid == wc_wpid && WIFEXITED(wc_status)
        ? WEXITSTATUS(ls_status)
        : -1;
}

编译并运行:

$ ls
a.out         error.log     ls-wc-pipes.c out.log       so-pipes.c
$ gcc ls-wc-pipe.c && ./aout
      8      65     449

您的结果将根据

ls-wc-pipe.c
运行的目录中的内容而有所不同。

© www.soinside.com 2019 - 2024. All rights reserved.