unix 如何使用fork、pipe和dup创建一个管道来连接两个子进程?

jljoyd4f  于 11个月前  发布在  Unix
关注(0)|答案(2)|浏览(181)

假设在一个程序中,我想执行两个进程,一个执行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]);
}

字符串
举个例子会很有帮助。

pod7payv

pod7payv1#

你的示例代码在语法和语义上都有问题(例如pipefd2没有decared,pipefd和pipefd2之间的混淆等等)。由于这看起来像是家庭作业,请确保你理解了我下面的注解,如果需要的话,请多问一些。我省略了pipe,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;
}

字符串

wvmv3b1j

wvmv3b1j2#

代码

我已经尽可能彻底地注解掉了代码。我还添加了一些处理进程分叉和命令执行的小错误处理。正如@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运行的目录中的内容而有所不同。

相关问题