针对 pipe(2) 的读取操作不会阻止进程

read operation against pipe(2) doesn't block the process

我正在研究 C 管道并阅读手册中的文档 (man 7 pipe) 我看到了这一行

If a process attempts to read from an empty pipe, then read(2) will block until data is available

所以我决定做一个简单的测试并试图了解发生了什么,但我发现尝试从管道读取的进程没有被阻止,我不明白为什么。你可以帮帮我吗?这是我使用的代码

    int main(){
    int pipefd[2];
    pid_t cpid;
    char buf;

    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid == 0) {    /* Child reads from pipe */
        close(pipefd[1]);          /* Close unused write end */
        printf("son: %i", getpid());

        while (read(pipefd[0], &buf, 1) > 0){
            write(STDOUT_FILENO, &buf, 1);
        }

        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);
        exit(EXIT_SUCCESS);

    } else {            /* Parent writes argv[1] to pipe */
        close(pipefd[0]);          /* Close unused read end */
        //write(pipefd[1], "this is an example", strlen("this is an example"));
        printf("father: %i", getpid());
        close(pipefd[1]);          /* Reader will see EOF */
        wait(NULL);                /* Wait for child */
        exit(EXIT_SUCCESS);
    }
}

read() 如果当前没有数据可用,从管道读取时将阻塞但另一端仍然打开.

这应该是有道理的:如果另一端仍然开放,那么作者可能最终会写一些东西,我们应该等待阅读。但是,如果另一端关闭,则无法再写入任何内容。如果我们阻塞,我们将永远阻塞,这将一事无成。所以 read returns 0 相反,类似于从常规文件读取时的 end-of-file 。同样,这是有道理的:我们已经完成了 parent 写入的数据,现在我们需要继续我们的生活。

您在这里观察到的正是普通程序中想要的:child 将打印出 parent 写入管道的所有内容,然后打印换行符和出口。在这种情况下,parent 不向管道写入任何内容,因此 child 不写入任何内容,然后是换行符并退出。

如果您想看到 child 块,请在 parent 的 close(pipefd[1]) 之前延迟,例如sleep(5);。但这除了作为演示之外毫无意义。

根据您的评论,完全删除 close(pipefd[1]) 绝对是错误的。 child直到写端关闭才会看到EOF,直到看到EOF才会退出。但是 parent 正在调用 wait(),并且在 child 退出之前不会继续,并且您有一个经典的 deadlock

另请注意,术语“损坏的管道”还有其他含义 - 它指的是进程尝试 写入 的管道的情况reading端已关闭。这两种情况不是对称处理的。真正的“破损管道”会导致写入进程接收到 SIGPIPE 信号,这通常会终止它。这个想法是,在正常情况下,我们会期望读取过程一直读取,直到所有数据都被消耗完。如果它在此之前关闭了读取端,则认为出现了问题,并且继续进行写入过程没有意义。