当父母和孩子都写入标准输出时,输出中没有交错

Posted

技术标签:

【中文标题】当父母和孩子都写入标准输出时,输出中没有交错【英文标题】:no interleave in output while both parent and child are writing to stdout 【发布时间】:2019-04-05 06:20:51 【问题描述】:

我正在尝试编写代码以查看如何使用 fork 创建子进程。由于子级从父级继承文件对象和描述符,如果子级和父级都写入标准输出,如果我理解正确,输出中应该有交错。在我编写的以下代码中,我在父项和子项中声明了两个字符串,并将它们写入标准输出。我观察到的是输出中没有交错。我错过了什么吗?

代码:

#include <unistd.h>
#include <stdio.h> 
#include <stdlib.h>
#include <sys/wait.h>

int main() 
  pid_t return_value;
  pid_t wait_p;
  if ((return_value = fork()) != 0) 
     char pa[15]= "Hi from parent\n";
     char pa_2[18]= "Hello from pariii\n";
     write(1, pa, 15);
     write(1, pa_2, 18);
   else 
     char c[17] = "Hello from child\n";
     char c_2[14] = "Hi from chiii\n";
     write(1, c , 17);
     write(1, c_2, 14);
  
  exit(0);

我机器上的输出(Ubutun 18.04,符合gcc):

Hi from parent
Hello from pariii
Hello from child
Hi from chiii

我看到 write 的原子性质如何导致进程不交错。但是为什么输出看起来好像父级先执行所有写入然后子级执行它的写入?另外,无论我尝试多少次,父母总是在孩子写之前写。

【问题讨论】:

“交错”?什么样的“交错”? 以下两个答案都是正确的。除非您的平台非常原始,否则您不太可能看到比行级交错更多的东西。更改您的输出方法以使用 putc() 之类的方法并使用 flush() 强制输出 可能 会导致您正在寻找的东西,但不能保证。 如果您在字符串中写入大量(兆字节,或至少数十千字节)数据(因此指定写入调用的大小),您可能(或可能不会)得到更有趣的结果将是许多千字节),并且输出将发送到管道或终端,但是使用如图所示的小字符串,您将看不到问题。 【参考方案1】:

write 系统调用是原子的;也就是说,这一切都是同时发生的。字符串没有机会交错。如果您要在多个部分中调用write(例如,写入字符串"Hi from ",然后写入字符串"parent""child",然后写入换行符),那么您可能会看到交错.当您将write 整个消息作为单个字符串时,这将永远不会发生。

请注意,更高级别的调用,如 printf 或类似的,具有更复杂的缓冲规则,因此可能有不同的规则。


在回答关于为什么两条父行总是在两条子行之前的附加问题时,这并不完全是运气,但不能保证。在具有相同设置的同一系统上,我希望无论是先安排父母还是先安排孩子,这几乎是一致的,但其他平台可能会做出相反的决定。

至于为什么它从不打印父级的一行,然后是子级的一行,然后是父级的第二行,我猜这与调度程序的细节和写入虚拟终端有关.从一个进程到另一个进程的上下文切换是昂贵的;如果write 足够快,调度程序可能会告诉它不应该这样做。也许如果您改为写入光盘,并在 writes 之间调用 sync 以确保实际涉及(相对较慢的)光盘,则它更有可能交错。或许不是;这真的很难预测。如果你真的想看到交错,我会至少写几个千字节,一次几个,来自父母和孩子;这几乎肯定会交错。

【讨论】:

【参考方案2】:

输出中应该有交错

不,输出可能是交错的。或者它可能不会。这取决于调度程序或月相。

【讨论】:

以上是关于当父母和孩子都写入标准输出时,输出中没有交错的主要内容,如果未能解决你的问题,请参考以下文章

阻止写入标准输出

如何防止孩子在 fork() 之后干扰父母的标准输入

C:从标准输出块中读取

shell基础

无法从另一个孩子读取一个孩子写入的共享内存

当标准输出没有输出时如何杀死 plink.exe? [复制]