针对 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 信号,这通常会终止它。这个想法是,在正常情况下,我们会期望读取过程一直读取,直到所有数据都被消耗完。如果它在此之前关闭了读取端,则认为出现了问题,并且继续进行写入过程没有意义。
我正在研究 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 信号,这通常会终止它。这个想法是,在正常情况下,我们会期望读取过程一直读取,直到所有数据都被消耗完。如果它在此之前关闭了读取端,则认为出现了问题,并且继续进行写入过程没有意义。