parent 和 child 都写入标准输出时输出无交错

no interleave in output while both parent and child are writing to stdout

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

代码:

#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

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

there should be interleave in the output

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

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

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


在回答为什么两条父线总是在两条子线之前的问题时,这不完全是运气,但不能保证。同一个系统,同样的设置,我认为先调度parent还是先调度child基本一致,但其他平台可能会做出相反的决定。

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