#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main( int argc, char *argv[] ){
FILE *fptr;
pid_t pid;
fptr = fopen("Shared File.txt", "a");
pid = fork();
if( pid > 0 ){ // parent process
int counter = 0;
while( counter < 10 ){
fprintf(fptr, "a");
++counter;
}
wait(NULL);
}
else{
int counter = 0;
while( counter < 5 ){
fprintf(fptr, "b");
++counter;
}
}
return 0;
}
当我执行此代码时,代码生成的文件包含以下消息:bbbbbaaaaaaaaa
每当我执行此代码时,我都会收到相同的消息。为什么进程不按洗牌顺序写入文件?
为什么操作系统首先尝试完成子进程?
我对消息的期望是这样的: baabbaaabaaabaa 进程之间没有连续的过渡。
父子进程之间的调度已经(至少)在When child processes are executed and How does fork system call really works中讨论过。
但在这种情况下,还有缓冲的问题
stdio
。您正在使用fprintf()
写入常规文件。默认情况下,stdio
缓冲输出到常规文件,直到写入足够的数据,以节省系统调用开销。在 x86 Linux 上,它通常似乎写入 4096 字节块,但您不能指望这一点,除非您手动设置缓冲(请参阅setbuf()
和朋友)。您可以使用
strace
显示程序进行的系统调用的命令来查看这一点。因此,虽然您无法预测哪个进程首先运行,但在这种情况下,您可以预测
a
s 是连续写入的,b
s 也是如此。你只能得到bbbbbaaaaaaaaaa
oraaaaaaaaaabbbbb
,你得到哪一个几乎取决于机会。我基本上同意@ikkachu,除了
你得到哪一个
bbbbbaaaaaaaaaa
或者aaaaaaaaaabbbbb
是可以预测的。由于 ,操作系统等待子进程完成,wait(NULL)
然后父进程退出。由于缓冲区在退出时被刷新,孩子首先开始写入。但是,不要依赖可预测的缓冲区。需要时使用显式刷新。
用户 ilkkachu 很好地解释了缓冲如何影响输出。我的回答描述了如果您消除缓冲会发生什么,例如通过将
fprintf
调用替换为对write
. 在这种情况下,您将得到严格交替a
的 s 和b
s。这是因为调用 会write
导致重新调度:写入一个进程阻塞,轮到另一个进程,等等。让我们想象一下如果 write 调用不会阻塞会发生什么。然后我们将不得不考虑时间尺度:您将获得更长的
a
s 和b
s 运行时间,而不仅仅是一次或两次,因为现代处理器每秒能够执行数十亿条指令,但调度频率通常在 100 Hz 之间和 1000 赫兹。一个进程在被抢占之前最多可以执行数千万条指令,而另一个进程被调度运行。即使考虑到系统调用开销,这也会给进程时间来打印非常长的连续a
s 或b
s 字符串。ikkachu 和 Johan 很好地解释了为什么你观察到你所做的行为,所以我稍微重写了你的程序,以便每个进程刷新流并休眠一秒钟(因此每个线程每次都有机会以不同的方式交错),以便您更清楚地看到交错效果。
如果你多次运行它,你会偶尔得到不同的结果: