使用 fork 和 dup2 管道命令

Pipe commands with fork and dup2

我写了下面的代码来传递两个命令:

#include <stdlib.h>
#include <unistd.h>

char    *program_1[3] = {"/bin/cat", "/dev/random", NULL};
char    *program_2[2] = {"/bin/ls", NULL};
char    *program_3[2] = {"/usr/bin/sort", NULL};

int main(void)
{
    int fd[2];
    int pid;

    pipe(fd);
    if ((pid = fork()) == 0) //Child process
    {
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        execve(program_3[0], program_3, NULL);
    }
    else if (pid > 0) //Parent process
    {
        dup2(fd[0], STDIN_FILENO);
        close(fd[1]);
        execve(program_2[0], program_2, NULL);
    }
    return (EXIT_SUCCESS);
}

每对 program_x / program_y 其中 x != y 工作正常,除了这个。 当我通过管道将 sort 放入 ls 时,ls 会在标准输出上很好地打印其输出,但随后,sort 会抛出此错误:sort: Input/output error.

当我在 bash 中键入 sort | ls 时,它会打印 ls 结果作为我的程序,然后等待输入。

我是不是做错了什么?

编辑:我正在尝试重新实现 shell 的行为

问题是当 ls 完成时,父进程将退出,这将关闭管道的读端,这将导致错误传播到管道的写端被 sort 检测到并写入错误消息。

它不会在 shell 中发生是因为 shells 处理管道的方式与您的简单示例程序不同,它使管道的右侧保持打开状态并且 运行 (可能在后台)直到你将 EOF (Ctrl-D) 传递给 sort 程序。

您的程序并不完全等同于 shell 通常所做的。

您正在用 ls 替换父项;而 shell 将创建 who 子进程并连接它们并等待它们完成。

更像是:

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

char    *program_2[2] = {"/bin/ls", NULL};
char    *program_3[2] = {"/usr/bin/sort", NULL};

int main(void)
{
    int fd[2];
    pid_t pid;
    pid_t pid2;

    pipe(fd);
    if ((pid = fork()) == 0) //Child process
    {
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        execve(program_3[0], program_3, NULL);
    }
    else if (pid > 0) //Parent process
    {
        if ( (pid2 = fork()) == 0) {
            dup2(fd[0], STDIN_FILENO);
            close(fd[1]);
            execve(program_2[0], program_2, NULL);
        }
    }

    waitpid(pid, 0, 0);
    waitpid(pid2, 0, 0);
    return (EXIT_SUCCESS);
}

我终于找到了解决方案,我们接近了:

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

char    *cat[3] = {"/bin/cat", "/dev/random", NULL};
char    *ls[2] = {"/bin/ls", NULL};
char    *sort[2] = {"/usr/bin/sort", NULL};

int main(void)
{
    int fd[2];
    pid_t pid;
    pid_t pid2;

    pipe(fd);
    if ((pid = fork()) == 0)
    {
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        execve(cat[0], cat, NULL);
    }
    else if (pid > 0)
    {
        if ( (pid2 = fork()) == 0)
        {
            dup2(fd[0], STDIN_FILENO);
            close(fd[1]);
            execve(ls[0], ls, NULL);
        }
        waitpid(pid2, 0, 0);
        close(fd[0]);
    }
    waitpid(pid, 0, 0);
    return (EXIT_SUCCESS);
}

我们需要在最后一个进程结束后关闭管道的读取端,这样,如果第一个进程尝试在管道上写入,则会抛出错误并退出进程,否则,如果仅从 stdin 读取为 sort,它将继续读取,因为 stdin 仍处于打开状态