【问题标题】:Read blocking even closing the Write ends读阻塞甚至关闭写结束
【发布时间】:2014-10-25 14:39:14
【问题描述】:
int  main()
{


    int p[2];
    int p1[2];
    pipe(p);
    pipe(p1);

    int pid,status;

    char buff[10000];

    pid = fork();

    if(pid == 0)
    { 
        close(p[0]);
        dup2(p[1],1);
        close(p[1]);

        char *argv[] = {"ls","-l",NULL};
        execv("/bin/ls",argv);
    }
    else
    {
        wait(&status);
        pid =fork();
        if (pid ==0)
        {
            close(p[1]);
            dup2(p[0],0);
            close(p[0]);

            close(p1[0]);
            dup2(p1[1],1);
            close(p1[1]);

            char *argv[] = {"uniq",NULL};

            execv("/bin/uniq",argv);
        }
        else
        {
            wait(&status);

            close(p1[1]);
            dup2(p1[0],0);
            close(p1[0]);

            char *argv[] = {"grep","^d",NULL};
            execv("/bin/grep",argv);
        }
    }
}

即使我已经正确关闭了两端(假设我已经关闭),为什么 read 在子进程中阻塞(uniq 进程)。尝试了 strace 并一直保持 3 到 4 小时。我仍然想知道为什么它会阻塞......任何帮助都会有所帮助:)

【问题讨论】:

    标签: c linux pipe fork system-calls


    【解决方案1】:

    问题是您没有关闭所有未使用的管道文件描述符。例如,在exec("/bin/grep", argv) 的最后一个分支中,您正在关闭p1[1]dup2()ing p1[0],但您没有关闭p[0]p[1]。因此,当 ls 完成对 uniq 的写入后,该管道将保持打开状态,因为您仍然有对它的悬空引用。

    您也没有检查任何系统调用是否有错误,这是应该的。

    这是一个修订版(稍作修改,因为 grepuniq 在我系统上的不同位置):

    #define _POSIX_C_SOURCE 200809L
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(void)
    {
        int p1to2[2];
        int p2to3[2];
    
        if ( pipe(p1to2) == -1 || pipe(p2to3) == -1 ) {
            perror("error calling pipe()");
            return EXIT_FAILURE;
        }
    
        pid_t pid;
    
        if ( (pid = fork()) == -1 ) {
            perror("error calling first fork()");
            return EXIT_FAILURE;
        }
        else if (pid == 0) {
            if ( close(p1to2[0]) == -1 ) {
                perror("error calling close() on p1to2[0]");
                return EXIT_FAILURE;
            }
            if ( p1to2[1] != STDOUT_FILENO ) {
                if ( dup2(p1to2[1], STDOUT_FILENO) == -1 ) {
                    perror("error calling dup2() on p1to2[1]");
                    return EXIT_FAILURE;
                }
                if ( close(p1to2[1]) == -1 ) {
                    perror("error calling close() on p1to2[1]");
                    return EXIT_FAILURE;
                }
            }
    
            if ( close(p2to3[0]) == -1 || close(p2to3[1]) == -1 ) {
                perror("error calling close() on p2to3");
                return EXIT_FAILURE;
            }
    
            char *argv[] = {"ls", "-l", NULL};
    
            if ( execv("/bin/ls", argv) == -1 ) {
                perror("couldn't execute /bin/ls");
                return EXIT_FAILURE;
            }
        } else {
            if ( (pid = fork()) == -1 ) {
                perror("error calling second fork()");
                return EXIT_FAILURE;
            }
            else if ( pid == 0 ) {
                if ( close(p1to2[1]) == -1 ) {
                    perror("error calling close() on p1to2[1]");
                    return EXIT_FAILURE;
                }
                if ( p1to2[0] != STDIN_FILENO ) {
                    if ( dup2(p1to2[0], STDIN_FILENO) == -1 ) {
                        perror("error calling dup2() on p1to2[0]");
                        return EXIT_FAILURE;
                    }
                    if ( close(p1to2[0]) == -1 ) {
                        perror("error calling close() on p1to2[0]");
                        return EXIT_FAILURE;
                    }
                }
    
                if ( close(p2to3[0]) == -1 ) {
                    perror("error calling close() on p2to3[0]");
                    return EXIT_FAILURE;
                }
                if ( p2to3[1] != STDOUT_FILENO ) {
                    if ( dup2(p2to3[1], STDOUT_FILENO) == -1 ) {
                        perror("error calling dup2() on p2to3[1]");
                        return EXIT_FAILURE;
                    }
                    if ( close(p2to3[1]) == -1 ) {
                        perror("error calling close() on p2to3[1]");
                        return EXIT_FAILURE;
                    }
                }
    
                char *argv[] = {"uniq", NULL};
    
                if ( execv("/usr/bin/uniq", argv) == -1 ) {
                    perror("couldn't execute /usr/bin/uniq");
                    return EXIT_FAILURE;
                }
            } else {
                if ( close(p1to2[0]) == -1 || close(p1to2[1]) == -1 ) {
                    perror("error calling close() on p1to2");
                    return EXIT_FAILURE;
                }
    
                if ( close(p2to3[1]) == -1 ){
                    perror("error calling close() on p2to3[1]");
                    return EXIT_FAILURE;
                }
                if ( p2to3[0] != STDIN_FILENO ) {
                    if ( dup2(p2to3[0], STDIN_FILENO) == -1 ) {
                        perror("error calling dup2() on p2to3[0]");
                        return EXIT_FAILURE;
                    }
                    if ( close(p2to3[0]) == -1 ) {
                        perror("error calling close() on p2to3[0]");
                        return EXIT_FAILURE;
                    }
                }
    
                char *argv[] = {"grep", "pipes", NULL};
    
                if ( execv("/usr/bin/grep", argv) == -1 ) {
                    perror("couldn't execute /usr/bin/grep");
                    return EXIT_FAILURE;
                }
            }
        }
    }
    

    和输出:

    paul@horus:~/src/sandbox$ ./pipes
    -rwxr-xr-x  1 paul  staff  8812 Oct 25 12:21 pipes
    -rw-r--r--  1 paul  staff  3817 Oct 25 12:21 pipes.c
    -rw-------  1 paul  staff   660 Oct 25 11:03 pipes.c.BAK
    paul@horus:~/src/sandbox$ 
    

    顺便说一句,当你有多个管道时,很容易混淆,close()s,dup2()s,以及像pp1这样的变量,特别是当你添加错误检查时需要做。这是一个很好的例子,说明将程序组合成函数可以极大地帮助并避免由于难以弄清楚发生了什么而引入错误。

    这是一个建议的组合,我建议main() 函数更容易理解、推理和排除故障,在这里:

    #define _POSIX_C_SOURCE 200809L
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    #include <unistd.h>
    
    void make_pipes(int * p, ...);
    pid_t fork_or_die(void);
    void close_pipe_pair(int * p);
    void make_std_reader(int * p);
    void make_std_writer(int * p);
    void execv_or_die(const char * path, char * const argv[]);
    
    /*  Main function  */
    
    int main(void)
    {
        pid_t pid;
        int p1to2[2], p2to3[2];
    
        make_pipes(p1to2, p2to3, NULL);
    
        if ( (pid = fork_or_die()) == 0 ) {
            make_std_writer(p1to2);
            close_pipe_pair(p2to3);
    
            char * args[] = {"ls", "-l", NULL};
            execv_or_die("/bin/ls", args);
        } else {
            if ( (pid = fork_or_die()) == 0 ) {
                make_std_reader(p1to2);
                make_std_writer(p2to3);
    
                char * args[] = {"uniq", NULL};
                execv_or_die("/usr/bin/uniq", args);
            } else {
                close_pipe_pair(p1to2);
                make_std_reader(p2to3);
    
                char * args[] = {"grep", "pipes", NULL};
                execv_or_die("/usr/bin/grep", args);
            }
        }
    }
    
    /*  Creates a pipe for each array in the NULL terminated arg list  */
    
    void make_pipes(int * p, ...)
    {
        va_list ap;
        va_start(ap, p);
        while ( p ) {
            if ( pipe(p) == -1 ) {
                perror("error calling pipe()");
                exit(EXIT_FAILURE);
            }
            p = va_arg(ap, int *);
        }
        va_end(ap);
    }
    
    /*  Calls fork() and exits on error  */
    
    pid_t fork_or_die(void)
    {
        pid_t p = fork();
        if ( p == -1 ) {
            perror("error calling fork()");
            exit(EXIT_FAILURE);
        }
        return p;
    }
    
    /*  Closes a pipe pair and exits on error */
    
    void close_pipe_pair(int * p)
    {
        if ( close(p[0]) == -1 || close(p[1]) == -1 ) {
            perror("error calling close() in close_pipe_pair()");
            exit(EXIT_FAILURE);
        }
    }
    
    /*  Closes the write end of a pipe and duplicates
     *  the read end into STDIN_FILENO, exiting on error  */
    
    void make_std_reader(int * p)
    {
        static const int read_end = 0;
        static const int write_end = 1;
    
        if ( close(p[write_end]) == -1 ) {
            perror("error calling close() in make_std_reader()");
            exit(EXIT_FAILURE);
        }
    
        if ( p[read_end] != STDIN_FILENO ) {
            if ( dup2(p[read_end], STDIN_FILENO) == -1 ) {
                perror("error calling dup2() in make_std_reader()");
                exit(EXIT_FAILURE);
            }
            if ( close(p[read_end]) == -1 ) {
                perror("error calling close() in make_std_reader()");
                exit(EXIT_FAILURE);
            }
        }
    }
    
    /*  Closes the read end of a pipe and duplicates
     *  the write end into STDOUT_FILENO, exiting on error  */
    
    void make_std_writer(int * p)
    {
        static const int read_end = 0;
        static const int write_end = 1;
    
        if ( close(p[read_end]) == -1 ) {
            perror("error calling close() in make_std_writer()");
            exit(EXIT_FAILURE);
        }
    
        if ( p[write_end] != STDOUT_FILENO ) {
            if ( dup2(p[write_end], STDOUT_FILENO) == -1 ) {
                perror("error calling dup2() in make_std_writer()");
                exit(EXIT_FAILURE);
            }
            if ( close(p[write_end]) == -1 ) {
                perror("error calling close() in make_std_writer()");
                exit(EXIT_FAILURE);
            }
        }
    }
    
    /*  Calls execv() and exits on error  */
    
    void execv_or_die(const char * path, char * const argv[])
    {
        if ( execv(path, argv) == -1 ) {
            perror("error calling execv()");
            exit(EXIT_FAILURE);
        }
    }
    

    【讨论】:

    • 谢谢保罗.. 将尝试它 n 回来。还将研究你建议的作文 n 将遵循它以使我的生活更轻松:)
    猜你喜欢
    • 1970-01-01
    • 2011-03-20
    • 2017-01-04
    • 2019-01-25
    • 2020-06-28
    • 2017-05-03
    • 1970-01-01
    • 1970-01-01
    • 2014-02-17
    相关资源
    最近更新 更多