以下 C 程序应该说明子进程和父进程之间的竞争条件:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
fork();
printf("\n 1234567890 \n");
return 0;
}
当我的朋友执行它时(在乌班图),他们得到预期的输出,这是混乱的 1234567890s
一个例子:12312345645678907890
但是当我在我的电脑上尝试相同的程序时架构Linux,它永远不会给出这样的输出。总是一个接着一个。
1234567890
1234567890
我喜欢拱门Linux是一些如何避免竞争条件的方法,但我想禁用任何这样的功能,并希望得到我朋友的输出。
答案1
该printf
调用将运行一个或多个write(2)
系统调用,它们的处理顺序将是输出的实际顺序。一个或多个,因为这取决于 C 库内部的缓冲。使用行缓冲输出(转到终端),您可能会收到两个write
调用,一次用于初始换行符,另一个用于其余部分。
write(1, "\n", 1);
write(1, " 1234567890 \n", 13);
可以在调用之间安排另一个进程,首先给出两个空行,然后给出带有数字的行,但由于没有进行太多处理,因此在卸载的系统上不太可能。
请注意,由于两个进程打印完全相同的内容,因此只要一个进程不中断另一个进程,哪个进程先执行并不重要。
如果输出进入文件或管道,默认情况下它是完全缓冲的,因此您可能只会得到一次write
调用(每个进程),并且没有机会混合输出。
如果数字是通过单独的系统调用一一输出的,则混合数字的示例是可能的。很难理解为什么明智的库实现在打印长度已知的静态字符串时会这样做。随着循环中的写入次数增多,混合输出更有可能出现:
像这样的东西:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
setbuf(stdout, NULL); /* explicitly unbuffered */
int x = fork();
for (i = 0 ; i < 500 ; i++) {
printf("%d", !!x);
}
if (x) {
wait(NULL);
printf("\n");
}
return 0;
}
给我如下的输出。大多数时候是这样,但并非总是如此。由系统决定如何安排进程。不可预测性是我们通常试图避免竞争条件的原因。
111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111111111111111111
111100000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000010000001001100
110000000011001100110011000000010011001100110000000100110011001100000001001
100110011000000010011001100110000000100110011001100000001001100110011000000
...
答案2
我怀疑fork()
系统调用使父进程或子进程等待足够长的时间,以允许另一个进程完成对 的调用printf()
并使字符串出现在输出中,甚至在到达其自己的 之前printf()
。
如果父进程和子进程都有时间同时执行循环,则在循环中输出大量字符串可能会显示您所描述的混合输出。
“修复”这个问题可能涉及重写fork()
系统调用或涉及其中的内核组件。