可能重复:
fork() and output
运行:
#include<stdio.h>
int main()
{
fork();
printf("b");
if (fork() == 0) {
write(1, "a", 1);
}else{
write(1, "c", 1);
}
return 0;
}
我得到cbcabbab
,有人可以向我解释输出吗?如果可能的话,是否有工具可以逐步查看运行程序?
答案 0 :(得分:1)
尝试再次运行它,你可能会获得不同的输出。
至于逐步查看程序的工具,我认为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
答案 1 :(得分:1)
简短回答:不要混用缓冲和无缓冲的代码。
答案很长:让我们使用以下命令测试源代码的变体:
$ 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"
,这是 {{1}之后的当然,打电话。就像在完全缓冲版本中一样,每个进程在write
缓冲区中都会有"b"
,因此您最终会看到其中的四个。
答案 2 :(得分:0)
除非你专门添加代码来同步你的分叉进程,否则它们将完全独立运行,因此输出顺序完全是“随机的”。进程调度将决定下一个运行的人,这又取决于系统中有多少个处理器核,运行的是什么,以及当前运行的每个进程运行了多长时间。
如链接中所述,您还可以从printf
的内部缓冲区获得输出,因为输出尚未写入代表stdout
的实际文件中 - 您可以“修复”该内容在fflush(stdout);
之后添加printf
。