Progear
Progear

Reputation: 167

Why waitpid is blocked with this particular case?

I want to understand how pipe works but i don't catch why waitpid is blocked in the case you execute my program with "ls -l /usr/bin" "grep ls".... If you take out the option -l it's works ! my_str_tab just put each word of the string into a charstar array.

void command_levels(int *pipe_fd, char **av, int idx, int pipe_save, int pipe_one)
{
    if (idx == 1)
        dup2(pipe_fd[1], 1);
    else if (idx > 1 && av[idx + 1] != NULL) {
        dup2(pipe_save, 0);
        dup2(pipe_fd[1], 1);
    }
    if (idx > 1 && av[idx + 1] == NULL) {
        dup2(pipe_save, 0);
        dup2(pipe_one, 1);
    }
}

void multiple_pipe_handle(char **av, char **env, int idx, int pipe_one)
{
    int pipe_fd[2] = {0, 0};
    char **command = NULL;
    static int pipe_save = 0;

    if (av[idx] == NULL)
        return;
    command = my_str_tab(av[idx], " ");
    pipe(pipe_fd);
    command_levels(pipe_fd, av, idx, pipe_save, pipe_one);
    if (fork() == 0) {
        close(pipe_fd[0]);
        close(pipe_fd[1]);
        execve(command[0], command, env);
    } else {
        wait(NULL);
        close(pipe_fd[1]);
        pipe_save = pipe_fd[0];
        multiple_pipe_handle(av, env, idx + 1, pipe_one);
        close(pipe_fd[0]);
    }
}

int main(int ac, char **av, char **env)
{
    int pipe_one = dup(1);

    multiple_pipe_handle(av, env, 1, pipe_one);
}

I expect the output of all word contains 'ls' but i'm in infinite loop..

Upvotes: 1

Views: 103

Answers (1)

zwol
zwol

Reputation: 140856

This is a common mistake when implementing pipelines.

When you pass -l to ls, it produces more output than when you don't pass that option. That makes it completely fill up the pipe's internal buffer. The kernel "blocks" it from continuing to execute until something reads from the other end of the pipe. But nothing is reading from the other end of the pipe yet, because your parent program is waiting for ls to finish execution before it starts the grep process. But ls will not finish execution until it can write more data to the pipe, so the overall program is deadlocked.

To fix this bug you must start all of the processes in the pipeline before you wait for any of them. You can't do that with a single recursive call to multiple_pipe_handle. You need two loops, one calling fork and one calling waitpid, and an array of subprocess PIDs. If you intend to read from the final pipeline process's output in your parent process, you must read all of the data produced (until read signals EOF by returning zero bytes of data) before you start calling waitpid.

Upvotes: 2

Related Questions