pipe()必须在fork()前调用以创建父子共享的管道,fd[0]为读端、fd[1]为写端;需正确关闭冗余端、处理SIGPIPE、循环读写并检查返回值,配合dup2()可重定向标准I/O。

pipe() 创建的文件描述符怎么用
父子进程通信用 pipe(),本质是创建一对内核缓冲区关联的文件描述符:fd[0] 是读端,fd[1] 是写端。调用后必须立即 fork(),否则子进程拿不到描述符副本——常见错误是先 fork() 再 pipe(),结果父子各自有一套互不相通的管道。
实操建议:
-
pipe()必须在fork()前调用,且检查返回值是否为 0(失败返回 -1) - 父进程通常关闭
fd[0](不用读),子进程关闭fd[1](不用写),避免资源泄漏和死锁 - 写入前确保对方已打开读端,否则
write()可能触发 SIGPIPE 或阻塞(取决于是否设了O_NONBLOCK)
父子进程如何避免阻塞或崩溃
默认管道是阻塞的:若读端没打开,write() 会直接终止进程(发送 SIGPIPE);若缓冲区满(通常 64KB),write() 阻塞;若写端已关而读端还在调 read(),会返回 0 表示 EOF。
关键控制点:
立即学习“C++免费学习笔记(深入)”;
- 子进程写完必须
close(fd[1]),否则父进程read()永远等不到 EOF - 父进程读取时用循环 + 判断返回值:返回 >0 是数据,=0 是对方关闭写端,-1 且
errno == EAGAIN才是无数据可读(需先设O_NONBLOCK) - 不要依赖管道容量,大块数据建议分次
write()并检查实际写入字节数
为什么 dup2() 经常和 pipe() 一起出现
当需要把管道对接到标准输入/输出(比如让子进程的 stdout 直接写入管道),就得用 dup2() 替换文件描述符。典型场景是实现 shell 管道命令,如 ls | wc -l。
注意细节:
-
dup2(fd[1], STDOUT_FILENO)后,子进程所有printf()、std::cout输出都会进管道,但必须在exec()前调用 -
dup2()会自动关闭目标 fd(如STDOUT_FILENO)再复制,无需手动close() - 如果忘了在
dup2()后close(fd[1]),会导致管道写端被重复持有,父进程永远收不到 EOF
C++ 中用 std::string 和 read()/write() 配合要注意什么
管道是字节流,没有消息边界。read() 和 write() 不保证一次传完全部数据,尤其跨进程时更不可靠。
实操要点:
- 用
std::string接收时,先resize()足够空间,再传&buf[0]给read()(C++11 起合法) -
write()返回值可能小于请求长度,必须用循环补全,否则数据截断 - 别直接
write(fd[1], str.c_str(), str.size())后就认为发完了——得检查返回值并重试 - 如果协议需要分隔符(比如换行),得自己加、自己解析,管道本身不提供此功能
最易被忽略的是:管道缓冲区大小有限,且父子进程调度不可控,任何假设“一次 read 就能读完一整条消息”的逻辑都会在压力下出错。











