为何子进程read函数未返回(预期返回-1)且printf函数失效?
问题分析与解决
咱们来一步步拆解你遇到的两个问题:子进程read一直阻塞不返回、printf没有正常输出。
1. 为什么read不返回-1反而一直阻塞?
管道的read返回-1(触发EOF)的核心前提是管道所有的写端都被关闭。你的代码里有两个关键错误导致了阻塞:
- 子进程继承了管道的写端
fd[1],但完全用不到这个描述符,也没有关闭它。这会让管道的写端始终处于“存在”状态,子进程的read会一直等待数据,不会触发EOF。 - 父进程写完管道后,既没关闭自己的写端
fd[1],还额外调用了read(fd[0])——但子进程根本没往管道写数据,且子进程的写端也没关闭,父进程的read自然也会卡住。
简单来说:管道的读写端没有按需关闭,导致所有read操作都卡在等待数据的状态。
2. 为什么printf无法正常工作?
不是printf功能有问题,是它根本没机会执行!不管子进程还是父进程,都是先调用read,而read已经阻塞住了,后面的printf代码根本没跑到。额外补充个知识点:printf是行缓冲机制,就算没阻塞,有时候也需要手动fflush(stdout)刷新,但在这个问题里,核心原因是代码根本没执行到printf那一步。
修正后的代码
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> // 引入waitpid所需的头文件 int main() { int fd[2]; pipe(fd); pid_t pid = fork(); if(pid == 0) { // 子进程:关闭不需要的写端 close(fd[1]); char buf[128] = {0}; int ret = read(fd[0], buf, sizeof buf); printf("Son ret is %d\n", ret); write(STDOUT_FILENO, buf, ret); // 测试read返回-1的场景:读完现有数据后再读一次 ret = read(fd[0], buf, sizeof buf); printf("Son second ret is %d\n", ret); // 读完后关闭读端 close(fd[0]); } else if(pid > 0){ // 父进程:关闭不需要的读端 close(fd[0]); char buf[128] = "hello\n"; // 字符串字面量自带结束符,无需手动加\0 // 写入实际有效字节数,而非整个buf的大小,避免写入多余的空字符 write(fd[1], buf, strlen(buf)); // 写完后立刻关闭写端,触发子进程的EOF close(fd[1]); // 等待子进程执行完毕,避免父进程先退出导致输出混乱 waitpid(pid, NULL, 0); printf("Dad finished\n"); } return 0; }
关键修正说明
- 按需关闭管道描述符:每个进程只保留自己需要的管道端,用不到的立刻关闭,这是管道编程的核心原则,能从根源避免阻塞问题。
- 控制写入字节数:用
strlen(buf)替代sizeof buf,只写入有效内容,避免把buf中多余的空字符写入管道。 - 等待子进程结束:父进程通过
waitpid等待子进程执行完再退出,避免输出顺序混乱或子进程变成僵尸进程。 - 移除无用的read:父进程原本的
read(fd[0])完全多余,因为子进程不会往管道写数据,只会导致阻塞,直接删除即可。
运行修正后的代码,你会看到子进程第一次read返回实际读取的字节数,第二次read就会返回-1,完全符合你的预期。
内容的提问来源于stack exchange,提问作者Yu Zhang
相关产品推荐
相关产品推荐

