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

fork后父进程无法读取stdin的原因是什么?

问题原因与解决方法

核心原因:C标准库stdio的缓冲区机制

你遇到的问题本质是stdio用户态缓冲区和内核文件描述符偏移量的交互导致的:

  • 当输入是管道(比如echo ... | ./my_program)时,stdio默认使用全缓冲模式——会一次性从内核读取尽可能多的数据(这里就是全部9个a)到用户态缓冲区。
  • fork之后,父子进程共享同一个内核文件描述符(偏移量是共享的),但各自拥有独立的stdio用户态缓冲区副本。
  • 子进程调用fgets(buf,5,stdin)时,因自身缓冲区为空,会触发系统调用把管道里的所有a读到自己的缓冲区,此时内核文件描述符的偏移量已走到EOF。
  • 子进程从缓冲区取走4个a后退出,剩下的5个a随子进程结束被销毁(用户态缓冲区属于进程私有资源)。
  • 父进程调用fgets时,自身缓冲区为空,尝试从内核读取数据,但此时内核管道已无数据,因此返回EOF。

解决方法

有两种可靠的方式解决这个问题:

方法1:禁用stdio缓冲区

在main函数开头添加一行代码,强制stdin使用无缓冲模式,让每次读取直接操作内核文件描述符:

setvbuf(stdin, NULL, _IONBF, 0);

修改后,子进程调用fgets会直接从内核读取4个字符,内核偏移量前进4位,父进程就能读取接下来的4个字符。

方法2:使用底层系统调用替代stdio函数

绕过stdio的缓冲区,直接用read系统调用读取数据:

// 子进程替换fgets的代码
read(STDIN_FILENO, buf, 4);
buf[4] = '\0'; // 手动添加字符串终止符
printf("Child : %s\n",buf);

// 父进程替换fgets的代码
ssize_t n = read(STDIN_FILENO, buf, 4);
if (n <= 0){
    printf("Parent : eof = %i\n",feof(stdin));
}
else {
    buf[n] = '\0';
    printf("Parent : %s\n",buf);
}

这种方式直接操作内核文件描述符,偏移量会正确共享,父子进程能按预期分走输入数据。

验证效果

修改后执行echo 'aaaaaaaaa' | ./my_program,会得到预期输出:

Child : aaaa
Parent : aaaa

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 13:15:17