管道在 C 中的实现

Dim*_*lov 3 c unix linux

我刚刚完成了我的 shell 解释器,但我认为我的管道实现是错误的。

它正在工作,像ls | cat -e工作这样的基本东西,但segmentation fault如果文件描述符超过 60 ko,我害怕可能性。当我做一个超过 60 ko 的文件的 cat 时,我也发现了一个无限循环。例如,如果 do a cat foo | cat -efoo 是一个长文件,则会发生无限循环。

或其他示例,当我执行cat /dev/urandom | cat -e此操作时不会向我显示任何显示,因此它首先执行cat /dev/urandom,然后执行cat -e.

这是我的代码:

int son(int *fd_in, int p[2], t_list *cmd, char **env)
{
    (void)env;
    dup2(*fd_in, 0);
    if (cmd->act != ENDACT && cmd->act != LEFT && cmd->act != DLEFT)
        dup2(p[1], 1);
    close(p[0]);
    execve(cmd->av[0], cmd->av, NULL);
    return (-1);
}

t_list *execute_pipe(t_list *cmd, int *fd_in)
{
    int           p[2];
    pid_t         pid;

    *fd_in = 0;
    while (cmd->act != -1)
    {
        pipe(p);
        if ((pid = fork()) == -1)
            return (NULL);
        else if (pid == 0)
            son(fd_in, p, cmd, NULL);
        else
        {
            wait(NULL);
            close(p[1]);
            *fd_in = p[0];
            if (cmd->act != PIPE)
                return (cmd);
            cmd = cmd->next;
        }
    }
    return (cmd);
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*ger 5

shell 管道的部分想法是所涉及的进程并发运行(或可能会运行)。您提供的代码通过wait()在启动下一个子进程之前对每个子进程进行 ing 来主动防止这种情况发生。除其他事项外,这会冒着在准备好耗尽它之前填充(操作系统级)管道缓冲区的风险。这将陷入僵局,或者,如果幸运的话,会产生错误。

在较高级别,该过程应如下所示:

  1. [shell]C最初是管道第一段的命令,并设置fd0STDIN_FILENO
  2. [贝壳]准备一个输出文件描述符:
    1. 如果有任何后续命令,创建一个pipe(),并设置fd1为该管道的写端;
    2. 否则,设置fd1STDOUT_FILENO
  3. [shell] fork()在其中运行 command 的子项C。在里面:
    1. [child] iffd0STDIN_FILENOthen不同dup2() fd0on STDIN_FILENOand closefd0
    2. [child] iffd1STDOUT_FILENOthen不同dup2() fd1on STDOUT_FILENOand closefd1
    3. [子] exec 命令C
  4. [shell]如果fd0不同于STDIN_FILENO则关闭fd0
  5. [shell] iffd1不同于STDOUT_FILENO则关闭fd1
  6. [贝壳]如果管道中有更多命令,则
    1. C为下一个命令
    2. fd0为上面步骤(2)中管道的读取端
    3. 转到第 2 步(准备输出文件描述符)
  7. [shell](此时管道中的所有进程都已启动。) wait()waitpid()所有子进程

请注意,对于包含任意正数命令(包括 1)的管道,这同样适用。