可能重复:
fork() 和输出
通过运行:
#include<stdio.h>
int main()
{
fork();
printf("b");
if (fork() == 0) {
write(1, "a", 1);
}else{
write(1, "c", 1);
}
return 0;
}
我得到了cbcabbab,有人可以向我解释一下输出吗?如果可能的话,是否有一个工具可以逐步查看运行过程?
可能重复:
fork() 和输出
通过运行:
#include<stdio.h>
int main()
{
fork();
printf("b");
if (fork() == 0) {
write(1, "a", 1);
}else{
write(1, "c", 1);
}
return 0;
}
我得到了cbcabbab,有人可以向我解释一下输出吗?如果可能的话,是否有一个工具可以逐步查看运行过程?
简短的回答:不要混合缓冲和非缓冲代码。
长答案:让我们使用以下命令测试源代码的变体:
$ rm dump
$ for X in 0 1 2 3 4 5 6 7 8 9; do for Y in 0 1 2 3 4 5 6 7 8 9; do for Z in 0 1 2 3 4 5 6 7 8 9; do echo `./program` >> dump; done; done; done
$ sort -u dump
这会执行program一千次,并列出它返回的所有唯一输出。
write为fwrite(或printf)#include <unistd.h>
#include <stdio.h>
int main()
{
fork();
printf("b");
if (fork() == 0) {
fwrite("a", 1, 1, stdout);
}else{
fwrite("c", 1, 1, stdout);
}
return 0;
}
这给出了一个非常规则的输出模式。事实上,只有六个输出是可能的:
bababcbc
babcbabc
babcbcba
bcbababc
bcbabcba
bcbcbaba
到底是怎么回事?
"b"两个进程都向stdout流写入一封信。默认情况下,流是缓冲的,因此 .stdout流具有相同的状态,因此也包含相同的缓冲区"b"。Y 和 Z 也是如此。stdout所有四个进程都向流中写入另一个字母。main,C 运行时接管。每个进程都会刷新它们的缓冲区,包括stdout.printf为write#include <unistd.h>
int main()
{
fork();
write(1, "b", 1);
if (fork() == 0) {
write(1, "a", 1);
}else{
write(1, "c", 1);
}
return 0;
}
可能的输出现在更加多样化了,但考虑到并发性,它仍然很容易理解:
bbacca
bbcaac
bbcaca
bbccaa
bcabca
bcbaca
这可能是您预期的输出。
您的代码比前两个变体提供了更多的结果:
cabbacbb
cabbcabb
cabbcbab
cabcabbb
cabcbabb
cabcbbab
... etc ...
这是因为write调用会立即产生输出,但缓冲的"b"只会在每个进程终止时打印,当然是在调用之后write。就像在完全缓冲的版本中一样,每个进程都会"b"在stdout缓冲区中拥有它,所以你最终会看到其中的四个。
再次尝试运行它,您可能会得到不同的输出。
至于逐步查看过程的工具,我认为strace -f可能会有所帮助:
$ strace -f ./weirdfork
execve("./weirdfork", ["./weirdfork"], [/* 35 vars */]) = 0
... uninteresting boiler plate removed ...
clone(Process 8581 attached
child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fe1c7d0b9d0) = 8581
[pid 8580] fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
[pid 8581] fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
[pid 8580] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <unfinished ...>
[pid 8581] mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 <unfinished ...>
[pid 8580] <... mmap resumed> ) = 0x7fe1c7d22000
[pid 8581] <... mmap resumed> ) = 0x7fe1c7d22000
[pid 8581] clone( <unfinished ...>
[pid 8580] clone(Process 8582 attached
<unfinished ...>
[pid 8581] <... clone resumed> child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fe1c7d0b9d0) = 8582
Process 8583 attached
[pid 8580] <... clone resumed> child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fe1c7d0b9d0) = 8583
[pid 8580] write(1, "c", 1 <unfinished ...>
[pid 8581] write(1, "c", 1cc) = 1
[pid 8580] <... write resumed> ) = 1
[pid 8581] write(1, "b", 1b <unfinished ...>
[pid 8580] write(1, "b", 1 <unfinished ...>
[pid 8581] <... write resumed> ) = 1
b[pid 8581] exit_group(0) = ?
Process 8581 detached
[pid 8580] <... write resumed> ) = 1
[pid 8580] exit_group(0) = ?
[pid 8583] write(1, "a", 1 <unfinished ...>
[pid 8582] write(1, "a", 1a) = 1
a[pid 8582] write(1, "b", 1 <unfinished ...>
[pid 8583] <... write resumed> ) = 1
[pid 8583] write(1, "b", 1b) = 1
[pid 8583] exit_group(0) = ?
Process 8583 detached
b<... write resumed> ) = 1
exit_group(0) = ?
Process 8582 detached
除非您专门添加代码来同步您的分叉进程,否则它们将完全独立运行,因此输出顺序是完全“随机的”。进程调度将决定谁下一个运行,这又取决于系统中有多少个处理器内核,还有什么正在运行,以及当前运行的每个进程已经运行了多长时间。
如链接中所述,您还可以从 的内部缓冲区获取输出printf,因为输出尚未写入代表的实际文件stdout- 您可以通过添加fflush(stdout);after来“修复”该问题printf。