Home > Blockchain >  Deadlock when performing reading and writing from pipe?
Deadlock when performing reading and writing from pipe?

Time:04-15

I want to create a pipeline using I/O redirection via dup2() and pipe() in order to pass the output of each subprocess as the input into the next subprocess. However, I'm noticing that even after I close all the pipes BOTH in the parent and child processes, the children do not terminate (which I suspect to be a deadlock when grep and/or cat performs reading and writing).

I also noticed that if I try to execute a pipeline where each subprocess is not dependent on the output of the previous subprocess (e.g. echo "hi" | echo "hello" | echo "123"), then the children exit successfully. This also seems to suggests that there is a deadlock when reading/writing from pipes after execvp() is called.

Am I correct in understanding the problem, and how can I fix the program to have its intended behavior?

I wrote a smaller program to demonstrate the control flow of what I discussed above.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>

int main() {    
    int fd[2][2];
    for(int i = 0; i < 2; i  ) {
        pipe(fd[i]);
    }
        
    for(int i = 0; i < 3; i  ) {
        int child_pid = fork();
        if(child_pid == 0) {
            if(i == 0) {
                dup2(fd[0][1], STDOUT_FILENO); 
                for(int i = 0; i < 2; i  ) {
                    close(fd[i][0]);
                    close(fd[i][1]);
                }
                char* argv[] = {"cat", "/usr/share/dict/words", NULL};
                execvp(argv[0], argv);
            }
            else if(i == 1) {
                dup2(fd[0][0], STDIN_FILENO);
                dup2(fd[1][1], STDOUT_FILENO); 
                for(int i = 0; i < 2; i  ) {
                    close(fd[i][0]);
                    close(fd[i][1]);
                }
                char* argv[] = {"grep", "yum", NULL};
                execvp(argv[0], argv);
            }
            else {
                dup2(fd[1][0], STDIN_FILENO);
                int rd = open("test.txt", O_RDWR | O_CREAT, 0777);
                dup2(rd, STDOUT_FILENO);
                for(int i = 0; i < 2; i  ) {
                    close(fd[i][0]);
                    close(fd[i][1]);
                }
                char* argv[] = {"grep", "yummi", NULL};
                execvp(argv[0], argv);
            }
        }
    }
    
    for(int i = 0; i < 2; i  ) {
        close(fd[i][0]);
        close(fd[i][1]);
    }
    
    return 0;
}

CodePudding user response:

I suggest you minimize the problem along these lines and verify that it doesn't hang. It looks it's the missing close(rd) that is tripping you up:

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    int fd[2];
    if(pipe(fd)) {
        printf("pipe failed\n");
        return 1;
    }
    pid_t pid = fork();
    if(pid == -1) {
        printf("fork failed\n");
        return 1;
    }
    if(!pid) {
        //printf("child\n");
        if(dup2(fd[1], STDOUT_FILENO) == -1) {
            printf("dup2 failed\n");
            return 1;
        }
        close(fd[0]);
        close(fd[1]);
        char *argv[] = {"cat", "/usr/share/dict/words", NULL};
        if(execvp(argv[0], argv) == -1) {
            printf("execvp failed\n");
            return 1;
        }
    }
    pid_t pid2 = fork();
    if(pid == -1) {
        printf("fork2 failed\n");
        return 1;
    }
    if(!pid2) {
        // printf("child2\n");
        if(dup2(fd[0], STDIN_FILENO) == -1) {
            printf("dup2 failed\n");
            return 1;
        }
        close(fd[0]);
        close(fd[1]);
        int rd = open("test.txt", O_RDWR | O_CREAT, 0777);
        if(dup2(rd, STDOUT_FILENO) == -1) {
            printf("dup2 failed\n");
            return 1;
        }
        close(rd);
        char *argv[] = {"grep", "yum", NULL};
        if(execvp(argv[0], argv) == -1) {
            printf("execvp failed\n");
            return 1;
        }
    }

    // printf("parent\n");
    close(fd[0]);
    close(fd[1]);
    int status;
    waitpid(pid, &status, 0);
    waitpid(pid2, &status, 0);
    return 0;
}

The next step would be to deduplicate this, say, by using functions for the shared parts.

  • Related