分叉-执行管道重定向问题

4

有人能告诉我这段代码有什么问题吗?

简单来说,它创建了输入和输出管道,并分叉执行了sort程序。父进程读取字典/usr/share/dict/words并将其写入到管道,该管道dup2()sort的标准输入中,同样地,父进程从管道中读取输出并将其打印到终端(父进程的标准输出)。或者至少应该是这样发生的。

回溯显示,父进程在第130行上的read()处停顿(带有'XXX'注释)。就像sort没有意识到文件末尾一样,但关闭pipeIn的写端应该“信号”这一点,对吧?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    int pipeIn[2];
    int pipeOut[2];

    if ((pipe(pipeIn)) == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    if ((pipe(pipeOut)) == -1)
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    pid_t child = fork();

    if (child == 0)
    {
        // This is child!

        if ((dup2(pipeIn[0], STDIN_FILENO)) == -1)
        {
            perror("dup2");
            exit(EXIT_FAILURE);
        }

        if ((dup2(pipeOut[1], STDOUT_FILENO)) == -1)
        {
            perror("dup2");
            exit(EXIT_FAILURE);
        }

        if ((dup2(pipeOut[1], STDERR_FILENO)) == -1)
        {
            perror("dup2");
            exit(EXIT_FAILURE);
        }

        if ((close(pipeIn[0])) == -1)
        {
            perror("close");
            exit(EXIT_FAILURE);
        }

        if ((close(pipeOut[1])) == -1)
        {
            perror("close");
            exit(EXIT_FAILURE);
        }

        if ((execlp("sort", "-r", NULL)) == -1)
        {
            perror("execlp");
            exit(EXIT_FAILURE);
        }
    }
    else if (child == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else
    {
        // This is parent!

        if ((close(pipeIn[0])) == -1)
        {
            perror("close");
            exit(EXIT_FAILURE);
        }

        if ((close(pipeOut[1])) == -1)
        {
            perror("close");
            exit(EXIT_FAILURE);
        }

        int dict = open("/usr/share/dict/words", O_RDONLY);

        if (dict == -1)
        {
            perror("open");
            exit(EXIT_FAILURE);
        }

        char buf[1024];
        int count;

        while ((count = read(dict, buf, sizeof(char) * 1024)) > 0)
        {
            putchar('.');

            if ((write(pipeIn[1], buf, count)) == -1)
            {
                perror("write 1");
                exit(EXIT_FAILURE);
            }
        }

        if (count == -1)
        {
            perror("read");
            exit(EXIT_FAILURE);
        }

        if ((close(dict)) == -1)
        {
            perror("close");
            exit(EXIT_FAILURE);
        }

        if ((close(pipeIn[1])) == -1)
        {
            perror("close");
            exit(EXIT_FAILURE);
        }

        while ((count = read(pipeOut[0], buf, sizeof(char) * 1024)) > 0) // XXX
        {
            putchar('!');

            if ((write(STDOUT_FILENO, buf, count)) == -1)
            {
                perror("write 2");
                exit(EXIT_FAILURE);
            }
        }

        if (count == -1)
        {
            perror("read");
            exit(EXIT_FAILURE);
        }

        if ((close(pipeOut[0])) == -1)
        {
            perror("close");
            exit(EXIT_FAILURE);
        }
    }

    return EXIT_SUCCESS;
}

感谢您的任何输入(请原谅双关语)。

我已经阅读过了,看起来相当不同。 - Doddy
我同意 - 相互引用的问题有些不同。 - Jonathan Leffler
通常情况下,您不会将 sort 的标准错误发送到管道中;您应该将其保持在与调用进程的标准错误相同的位置(很可能是终端)。 - Jonathan Leffler
1个回答

3

您的问题是在子进程中没有关闭未使用的管道端点。因此,您需要在exec之前的某个位置添加以下代码:

    if ((close(pipeIn[1])) == -1)
    {
        perror("close");
        exit(EXIT_FAILURE);
    }

    if ((close(pipeOut[0])) == -1)
    {
        perror("close");
        exit(EXIT_FAILURE);
    }

+1 - 你说得对。这很反直觉,但是如果你在子进程中使用管道作为标准输入或标准输出,那么通常需要关闭pipe()返回的两个文件描述符。 - Jonathan Leffler
是的,一开始我并不理解,但现在想了想就有点明白了(之前从未想过这个问题)。在fork之后,实际上会有两个打开的文件描述符可以写入管道。因此,在所有用于写入端的文件描述符都被关闭之前,从管道读取时是无法收到“eof”的。 - Sodved
谢谢,它正在工作,但它没有打印单词列表的反向顺序。 execlp(“sort”,“-r”,NULL); 的语法是否有误? - Doddy
哦,它需要是 execlp("sort", "sort", "-r", NULL); - 我不知道为什么! - Doddy
1
@bean 因为 argv[0] 总是进程的名称。 允许您控制它在 ps 和其他进程信息中的显示方式。 - Sodved

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接