我试图编写代码来了解如何使用fork创建子进程。由于子对象从父对象继承文件对象和描述符,如果子对象和父对象都写入stdout,如果我理解正确,输出中应该有交错。在我编写的以下代码中,我在parent和child中声明了两个字符串,并将它们写入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 parentn";
char pa_2[18]= "Hello from pariiin";
write(1, pa, 15);
write(1, pa_2, 18);
} else {
char c[17] = "Hello from childn";
char c_2[14] = "Hi from chiiin";
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
或类似的高级调用具有更复杂的缓冲规则,因此可能具有不同的规则。
在回答关于为什么两个父行总是在两个子行之前的附加问题时,这并不完全是运气,但不能保证。在相同设置的同一系统上,我预计无论是先安排家长还是先安排孩子,都会基本一致,但其他平台可能会做出相反的决定。
至于为什么它从不打印父级的一行,然后打印子级的一行将,然后打印父级第二行,我想这与调度器的细节和写入虚拟终端有关。从一个进程到另一个进程的上下文切换是昂贵的;如果CCD_ 8足够快,则调度器可能能够告诉它不应该这样做。也许,如果您改为写入光盘,并在write
秒之间调用sync
,以确保(相对较慢的(光盘确实参与其中,则它更有可能交错。或者也许不是;这真的很难预测。如果你真的想看到交错,我会从父母和孩子那里写至少几千字节,一次写几个;这几乎肯定会交织。
输出中应该有交错
否,输出可能是交错的。或者可能不会。这取决于日程安排,或者月相。