子进程通过dup2()将pipe文件描述符重定向到stdin/stdout:父进程先pipe()再fork(),子进程用dup2(pipefd[0],stdin_fileno)连stdin、dup2(pipefd[1],stdout_fileno)连stdout,随后必须close所有原始pipe fd,否则导致eof不触发或阻塞。

子进程的 stdin/stdout 怎么连到 pipe?
核心是用 dup2() 把 pipe 的文件描述符“覆盖”到子进程的标准流上,而不是靠 shell 重定向语法。父进程先 pipe() 创建一对 fd,再 fork(),然后在子进程中调用 dup2() 替换 STDIN_FILENO 或 STDOUT_FILENO,最后关闭所有未使用的 pipe fd。
常见错误是:子进程没关掉 pipe 的另一端,导致父进程 read() 永远阻塞(因为内核认为写端还开着);或者父进程在 fork() 前就关闭了 pipe fd,子进程根本拿不到。
- 读端 pipe[0] → 子进程
stdin:子进程调dup2(pipefd[0], STDIN_FILENO) - 写端 pipe[fd1] → 子进程
stdout:子进程调dup2(pipefd[1], STDOUT_FILENO) - 子进程必须
close()所有原始 pipe fd(包括自己不用的那一端) - 父进程也必须关掉自己不用的 pipe fd,否则会影响 EOF 判定
exec() 前为什么要 dup2()?
因为 exec() 系列函数不会改变已打开的文件描述符——它只替换当前进程的代码和数据段,fd 表完全保留。所以必须在 exec() 之前把标准流重定向好,否则子进程启动后仍用默认的终端 fd。
典型顺序:fork() → 子进程 close() 无关 fd → dup2() → close() 原 pipe fd → exec()。父进程则相反:关掉自己不用的 pipe 端,用 read()/write() 和子进程通信。
完整流程:Reddit 痛点扫描 → 聚类 → 构建 pip 可安装的 CLI 工具 → 推送到 GitHub。使用此模式已交付 5 款工具,经验证 343 条痛点。
- 如果漏掉
dup2(),exec()后的程序依然从终端读、向终端写 - 如果
dup2()后不close()原 pipe fd,会导致资源泄漏 + EOF 不触发 - 不能用
freopen():那是 C 标准库 FILE* 层,对底层 fd 无影响,且子进程 exec 后 FILE* 全失效
为什么父进程 read() 会卡住,等不到 EOF?
因为内核只有在“所有写端 fd 都关闭”时,才向读端返回 0 字节(即 EOF)。如果父进程自己没关 pipe[1],或子进程忘了关 pipe[0],哪怕子进程已 exit,读端仍会阻塞。
调试时可用 lsof -p <pid></pid> 看进程还持有哪些 pipe fd。典型修复方式是:父进程在 fork() 后立刻 close(pipefd[0])(如果只用来写),子进程在 dup2() 后立刻 close(pipefd[0]); close(pipefd[1])(只留重定向后的标准流)。
- 子进程若用
exit()正常退出,其所有 fd 自动关闭,但前提是它没泄漏 pipe fd - 父进程若要多次写入并分批读取,需确保每次写完都
close(pipefd[1])再 reopen(不推荐),更稳妥是用waitpid()等子进程结束,再一次性read() - 管道缓冲区大小通常为 64KB(Linux),写超限会阻塞,注意控制数据量
完整最小可运行示例的关键点
下面这段逻辑能跑通,但删掉任意一个 close() 就可能出问题:
int pipefd[2];
pipe(pipefd);
pid_t pid = fork();
if (pid == 0) {
// child
dup2(pipefd[0], STDIN_FILENO); // 读端 → stdin
dup2(pipefd[1], STDOUT_FILENO); // 写端 → stdout(实际不会用到,仅示意)
close(pipefd[0]); // 必须关
close(pipefd[1]); // 必须关
execlp("wc", "wc", "-l", nullptr);
} else {
// parent
close(pipefd[0]); // 父不读,关读端
write(pipefd[1], "hello\nworld\n", 13);
close(pipefd[1]); // 关写端,wc 才会输出并 exit
waitpid(pid, nullptr, 0);
}
最易被忽略的是:子进程里 dup2() 成功后,原 pipe fd 必须显式 close() —— 它们不是自动失效的,C++ 不会帮你管理 C 风格 fd。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










