c - 读取阻塞甚至关闭写入结束

标签 c linux pipe fork system-calls

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

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

最佳答案

问题是您没有关闭所有未使用的管道文件描述符。例如,在您的最后一个分支中 exec("/bin/grep", argv) ,你正在关闭 p1[1]dup2()荷兰国际集团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()小号,dup2() s,以及像 p 这样的变量和 p1 ,特别是当您添加您需要进行的错误检查时。这是一个很好的例子,说明将程序组合成函数可以提供巨大的帮助,并避免由于难以弄清楚发生了什么而引入错误。

这是一个建议的构图,我建议 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);
    }
}

关于c - 读取阻塞甚至关闭写入结束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26563553/

相关文章:

c - 为什么我的线程不在程序完成之前将其结果存储在数组中?

c++ - 在 C++ 中包含 C 代码

c - 使用 Wininet 发送多个 Http 请求

c - CodeBlocks IDE制作的C语言程序,Windows下双击运行,Ubuntu上不行。为什么?

c - 在 C for Linux 中使用 fork() 进行阶乘计算

linux - 无法获取 Chef Recipe 以获取 .bashrc,我做错了什么?

linux - 我是否需要在 Windows 中使用 Hadoop 来连接在 Linux 上运行的 hbase?

pipe - xargs: tar: 由信号 13 终止

bash 权限被拒绝 : Can't echo to the stdin of a running process?

bash - 将脚本的输出作为独立的 bash 命令运行