You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

管道通信中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操作的时机。

第一段代码的问题分析

  1. 管道写端被提前关闭:父进程在fork子进程前就执行了close(pfd[1]),此时管道写端的所有引用都被关闭(父进程的写端已关闭,子进程还未创建)。
  2. 子进程持有无效文件描述符:fork会复制父进程的文件描述符表,因此所有子进程继承的pfd[1]都是已关闭的无效描述符。
  3. write调用失败:子进程调用write(pfd[1], ...)时,会因为文件描述符无效(errno=EBADF)返回-1;部分情况下还会触发SIGPIPE信号,直接终止子进程,导致连打印语句都无法执行。
  4. 父进程read返回0:由于管道写端已全部关闭,父进程的read会返回0(表示到达EOF),无法读取到任何有效数据。

第二段代码的正常逻辑

  1. 子进程持有有效写端:父进程在所有子进程fork完成后才关闭pfd[1],因此每个子进程都继承了打开的管道写端描述符。
  2. write无需等待read:sizeof(int)远小于管道默认缓冲区大小(通常为4096字节),write会直接将数据写入缓冲区,立刻返回成功写入的字节数,无需等待父进程的read操作。
  3. 父进程正常读取:父进程关闭写端后,依次读取管道缓冲区中的数据,此时所有子进程已完成写入,因此read可以成功读取到每一条数据。

管道的核心规则

  • 当所有写端都关闭时,read会返回0(EOF)。
  • 当所有读端都关闭时,write会触发SIGPIPE信号,默认终止进程;若捕获该信号,write返回-1且errno=EPIPE。
  • 往已关闭的文件描述符写入,write返回-1且errno=EBADF。

内容的提问来源于stack exchange,提问作者Sterling Mcleod

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:02:31