从管道执行读写时出现死锁?

Deadlock when performing reading and writing from pipe?

我想通过 dup2() 和 pipe() 使用 I/O 重定向创建管道,以便将每个子进程的输出作为输入传递到下一个子进程。但是,我注意到即使在关闭父进程和子进程中的所有管道之后,子进程也不会终止(我怀疑这是 grep and/or cat 执行读写时的死锁)。

我还注意到,如果我尝试执行一个管道,其中每个子进程都不依赖于前一个子进程的输出(例如 echo "hi" | echo "hello" | echo "123"),那么子进程成功退出。这似乎也表明在调用 execvp() 后管道 reading/writing 时存在死锁。

我对问题的理解是否正确,如何修复程序使其具有预期的行为?

我写了一个较小的程序来演示上面讨论的控制流程。

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

我建议您按照这些思路将问题最小化并确认它没有挂起。看起来是缺少的 close(rd) 让您感到困惑:

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

下一步将是删除重复数据,例如,通过对共享部分使用函数。