Pipe implementation in C

I just finished a shell interpreter but I think my channel implementation is wrong.

It works, the main things seem to ls | cat -e

work, but I'm afraid of the possibilities segmentation fault

if the file descriptor is more than 60 k. I also found an infinite loop when I make a cat of a file longer than 60 kb. For example, if do cat foo | cat -e

, where foo is a long file, an endless loop occurs.

Or another example, when I do cat /dev/urandom | cat -e

, it doesn't show me any display, so it executes first cat /dev/urandom

and then cat -e

.

This is my code:

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);
}

      

+3


source to share


1 answer


Part of the idea behind a shell pipeline is that the involved processes run concurrently (or can do). The code you provided actively prevents this from wait()

happening for each child process before starting the next one. Among other things, there is a risk of filling the buffer (OS-level) before anything is ready to drain it. It will stall or, with luck, generate an error.

At a high level, the procedure should look like this:



  • [shell] Let C

    initially be the command for the first channel segment and set fd0

    asSTDIN_FILENO

  • [shell] . Prepare an output file descriptor:
    • If there are any follow-up commands, create pipe()

      and set fd1

      as the end of this channel's recording;
    • otherwise set fd1

      asSTDOUT_FILENO

  • [shell] fork()

    child element to run the command C

    . In that:
    • [child] if fd0

      different from STDIN_FILENO

      , then dup2()

      fd0

      on STDIN_FILENO

      and closefd0

    • [child] if fd1

      different from STDOUT_FILENO

      , then dup2()

      fd1

      on STDOUT_FILENO

      and closefd1

    • [child] exec commandC

  • [shell] if fd0

    different from STDIN_FILENO

    , then closefd0

  • [shell] if fd1

    different from STDOUT_FILENO

    , then closefd1

  • [shell] If there are more commands in the pipe, then
    • set C

      will be the next command
    • set fd0

      as read pipe end from step (2) above
    • go to step 2 (prepare an output file descriptor)
  • [shell] (At this point, all processes in the pipeline have been started.) wait()

    or waitpid()

    for all child processes

Note that this works equally well for a pipeline containing any positive number of instructions, including 1.

+3


source







All Articles