Possible Duplicate:
fork() and output
by running:
#include<stdio.h>
int main()
{
fork();
printf("b");
if (fork() == 0) {
write(1, "a", 1);
}else{
write(1, "c", 1);
}
return 0;
}
I got cbcabbab
, could someone explain the output to me? And if possible, is there a tool to see the running procedure step by step?
Try running it again, you'll probably get a different output.
As for a tool to see the procedure step by step, I think strace -f
might help a bit:
$ 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
Short answer: don't mix buffered and unbuffered code.
Long answer: let's test variants of your source code using the following commands:
$ 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
This executes program
a thousand times, and lists all unique outputs it returned.
Buffered version: replace write
by fwrite
(or 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;
}
This gives a very regular pattern of output. In fact, only six outputs are possible:
bababcbc
babcbabc
babcbcba
bcbababc
bcbabcba
bcbcbaba
What is going on?
- After the first fork, there are two processes, W and Y.
- Both processes write a letter
"b"
to the stdout
stream. The stream is buffered by default, so .
- After second fork, there are four processes: W and X, Y and Z. The
stdout
streams of W and X have the same state, so also the same buffer containing just "b"
. The same holds for Y and Z.
- All four processes write another letter to the
stdout
stream.
- After
main
returns, the C runtime takes over. Every process flushes their buffers, including the buffers of stdout
.
Unbuffered version: replace printf
by 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;
}
The possible output is now a little more varied, but it's still pretty understandable, given concurrency:
bbacca
bbcaac
bbcaca
bbccaa
bcabca
bcbaca
This is probably the output you expected.
Mixed version (yours)
Your code gives many more results than the previous two variants:
cabbacbb
cabbcabb
cabbcbab
cabcabbb
cabcbabb
cabcbbab
... etc ...
This is because the write
call will produce output immediately, but the buffered "b"
will only be printed when each process terminates, which is after the write
call, of course. Just like in the fully buffered version, every process will have that "b"
in the stdout
buffer, so you'll end up seeing four of them.
Unless you specifically add code to synchronize your forked processes, they will run completely independently, and thus the order of output is completely "random". The process scheduling will decide who gets to run next, which in turn depends on how many processor cores there are in the system, what else is running, and how long each process that is currently running has been running for.
As explained in the link, you also get output from the internal buffers of printf
, since the output has not yet been written to the actual file representing the stdout
- you can "fix" that by adding a fflush(stdout);
after printf
.