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

#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系统调用是原子的;也就是说,这一切都是同时发生的。没有机会将字符串交错。如果要在多个部分中进行write调用(例如,编写字符串"Hi from ",然后编写字符串"parent""child",然后编写换行符),则可能会看到交错。当您将整个消息作为单个字符串时,这种情况将永远不会发生。
注意,像write或类似的高级调用有更复杂的缓冲规则,因此可能有不同的规则。
在回答为什么两个父行总是在两个子行之前的问题时,这并不完全是运气,但也不能保证。在具有相同设置的同一个系统上,我预计无论是先安排父级还是先安排子级,都基本一致,但其他平台可能会做出相反的决定。
至于为什么它从不从父级打印一行,然后从子级打印一行,然后从父级打印第二行,我想这与调度程序的细节和写入虚拟终端有关。从一个进程到另一个进程的上下文切换是昂贵的;如果printf足够快,调度程序可能会告诉它不应该这样做。也许如果你改为给disc写信,并在writes之间调用sync以确保(相对较慢的)disc确实参与进来,那么它更可能交叉存取。或许不是,这真的很难预测。如果你真的想看到交错,我会从父对象和子对象中写下至少几千字节,一次写几个;这几乎可以确定是交错的。

09-06 20:13