【问题标题】:How to use pipes correctly with child processes?如何在子进程中正确使用管道?
【发布时间】:2021-07-19 16:23:07
【问题描述】:
//I want to execute pwd|sort > file.txt

char commands[2][30] = {"pwd", "sort"};
char directory [2][30] = {"/usr/bin/pwd", "/usr/bin/sort"}
char outputFile[30] = "file.txt"
int totalCommands = 2;
bool isOutputFilePresent = true;

//creating two pipes
int fd1[2];
int fd2[2];
pipe(fd1);
pipe(fd2);
//Closing the writing ends but keeping the reading ends open as closing them will destroy both the pipes
close (fd1[1]);
close (fd2[1]);

//loop to execute each command by creating a child process in each iteration
for (int i = 0; i < totalCommands; ++i)
{
    pid_t pid = fork();
    if (pid == 0)
    {
        if (i == totalCommands - 1)
        {
            if (i%2 == 0)
            {
                close(fd1[1]);
                close(fd2[0]);
                dup2(fd1[0], 0);
                dup2(fd2[1], 1);
            }
            else
            {
                close(fd2[1]);
                close(fd1[0]);
                dup2(fd2[0], 0);
                dup2(fd1[1], 1);
            }
        }
        else
        {
            if (i%2 == 0)
            { 
                close(fd2[0]);
                close(fd2[1]);
                close(fd1[1]);
                dup2(fd1[0], 0);
            }
            else
            {
                close(fd1[0]);
                close(fd1[1]);
                close(fd2[1]);
                dup2(fd2[0], 0);
            }
            if(isOutputFilePresent)
            {
                int outputFD = open (outputFile, O_WRONLY | O_CREAT);
                dup2(outputFD, 1);
            }

        }
        execv(directory[i], commands[i]) //ignore the fact that i am passing command name instead of argument vector
    }
    wait(NULL);
}

由于子进程将继承它自己的管道 FD,我关闭了每个进程中未使用的管道,并使用 dup2 为其分配了 stdout 和 stdin FD。但是这段代码有一个逻辑错误,我猜是因为没有正确使用管道。那么我在管道的概念上哪里弄错了,这个问题的解决方案是什么。谢谢!

【问题讨论】:

    标签: c pipe unistd.h


    【解决方案1】:

    在打开管道后立即关闭管道的写入端。因此,没有任何东西可以写入这些管道:

    pipe(fd1);
    pipe(fd2);
    //Closing the writing ends but keeping the reading ends open as closing them will destroy both the pipes
    close (fd1[1]);
    close (fd2[1]);
    

    这样做:

    1. 创建两个管道:A 和 B
    2. 派生/派生新的子进程
    3. Parent 关闭 A 的写端和 B 的读端
    4. 子级关闭 A 的读取端和 B 的写入端

    因此,孩子向 A 写入,而父母从 A 读取。 父母向 B 写入,孩子从 B 读取。

    用于 pipe(2) 的 man page 为您提供示例代码,向您展示如何执行此操作:

    #include <sys/types.h>
    #include <sys/wait.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    int
    main(int argc, char *argv[])
    {
       int pipefd[2];
       pid_t cpid;
       char buf;
    
       if (argc != 2) {
           fprintf(stderr, "Usage: %s <string>\n", argv[0]);
           exit(EXIT_FAILURE);
       }
    
       if (pipe(pipefd) == -1) {
           perror("pipe");
           exit(EXIT_FAILURE);
       }
    
       cpid = fork();
       if (cpid == -1) {
           perror("fork");
           exit(EXIT_FAILURE);
       }
    
       if (cpid == 0) {    /* Child reads from pipe */
           close(pipefd[1]);          /* Close unused write end */
    
           while (read(pipefd[0], &buf, 1) > 0)
               write(STDOUT_FILENO, &buf, 1);
    
           write(STDOUT_FILENO, "\n", 1);
           close(pipefd[0]);
           _exit(EXIT_SUCCESS);
    
       } else {            /* Parent writes argv[1] to pipe */
           close(pipefd[0]);          /* Close unused read end */
           write(pipefd[1], argv[1], strlen(argv[1]));
           close(pipefd[1]);          /* Reader will see EOF */
           wait(NULL);                /* Wait for child */
           exit(EXIT_SUCCESS);
       }
    }
    

    【讨论】:

    • 如果我们关闭管道的一端并调用fork,子进程是否也会继承它作为关闭?
    • 自己试试看:)(我相信答案是孩子继承了open文件句柄。关闭的不会重新打开。)跨度>
    猜你喜欢
    • 2019-08-19
    • 2017-02-18
    • 2014-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-10
    相关资源
    最近更新 更多