管道通信中write系统调用返回值是否依赖read读取操作?
父子进程管道中write返回值的疑问
抱歉提出基础问题,但我在文档或生成式AI中找不到易懂的直接答案。在父子进程间的管道中调用write时,是否只有当数据被read系统调用读取后,write才会返回非-1值?
第一段代码(输出异常)
int main(int argc, char* argv[]) { int workers = 4; int pfd[2]; int pipe_result = pipe(pfd); if(pipe_result != 0) { fail("Can't create pipe"); } // Parent doesn't need write channel close(pfd[1]); for(int i = 0; i < workers; i++) { pid_t pid = fork(); // Child if(pid == 0) { // Child doesn't need read channel close(pfd[0]); int count = 101; // Write count to pipe int len_sent = write(pfd[1], &count, sizeof(count)); printf("\nChild id: %d Sent %d bytes, count: %d", getpid(), len_sent, count); // Exit so that the child processes don't loop close(pfd[1]); exit(0); } else { int par_count; // Read int len = read(pfd[0], &par_count, sizeof(par_count)); printf("\nlen: %d par_count: %d", len, par_count); } } // Close read buffer in parent close(pfd[0]); return 0; }
这段代码通常输出显示0或1次write和read调用成功。
第二段代码(输出正常)
int main(int argc, char* argv[]) { int workers = 4; int pfd[2]; int pipe_result = pipe(pfd); if(pipe_result != 0) { fail("Can't create pipe"); } for(int i = 0; i < workers; i++) { pid_t pid = fork(); // Child if(pid == 0) { // Child doesn't need read channel close(pfd[0]); int count = 101; // Write count to pipe int len_sent = write(pfd[1], &count, sizeof(count)); printf("\nChild id: %d Sent %d bytes, count: %d", getpid(), len_sent, count); // Exit so that the child processes don't loop close(pfd[1]); exit(0); } } // Parent doesn't need write channel close(pfd[1]); // Parent for(int i = 0; i < workers; i++) { int par_count; // Read int len = read(pfd[0], &par_count, sizeof(par_count)); printf("\nlen: %d par_count: %d", len, par_count); } // Close read in parent close(pfd[0]); return 0; }
将read操作移至所有write完成后执行,则所有调用均正常。
为何这种改动会影响write调用的返回值?我理解其对read返回值的影响,但不明白为何会导致write返回-1。write的返回值是否依赖于read调用读取数据?
解答
首先明确核心结论:write的返回值不依赖于read是否读取数据,两段代码的差异根源是管道写端的有效性,而非read操作的时机。
第一段代码的问题分析
- 管道写端被提前关闭:父进程在fork子进程前就执行了
close(pfd[1]),此时管道写端的所有引用都被关闭(父进程的写端已关闭,子进程还未创建)。 - 子进程持有无效文件描述符:fork会复制父进程的文件描述符表,因此所有子进程继承的
pfd[1]都是已关闭的无效描述符。 - write调用失败:子进程调用
write(pfd[1], ...)时,会因为文件描述符无效(errno=EBADF)返回-1;部分情况下还会触发SIGPIPE信号,直接终止子进程,导致连打印语句都无法执行。 - 父进程read返回0:由于管道写端已全部关闭,父进程的
read会返回0(表示到达EOF),无法读取到任何有效数据。
第二段代码的正常逻辑
- 子进程持有有效写端:父进程在所有子进程fork完成后才关闭
pfd[1],因此每个子进程都继承了打开的管道写端描述符。 - write无需等待read:
sizeof(int)远小于管道默认缓冲区大小(通常为4096字节),write会直接将数据写入缓冲区,立刻返回成功写入的字节数,无需等待父进程的read操作。 - 父进程正常读取:父进程关闭写端后,依次读取管道缓冲区中的数据,此时所有子进程已完成写入,因此read可以成功读取到每一条数据。
管道的核心规则
- 当所有写端都关闭时,read会返回0(EOF)。
- 当所有读端都关闭时,write会触发
SIGPIPE信号,默认终止进程;若捕获该信号,write返回-1且errno=EPIPE。 - 往已关闭的文件描述符写入,write返回-1且
errno=EBADF。
内容的提问来源于stack exchange,提问作者Sterling Mcleod
相关产品推荐
相关产品推荐

