Here's one concrete example why. Predict the output of the following program:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char** argv) {
printf("hello world\n");
if (fork() == 0) {
printf("child\n");
} else {
printf("parent\n");
}
return 0;
}
with the following:
dyoo@dyoo-desktop:~/work/net-2$ gcc -Wall test-fork.c
dyoo@dyoo-desktop:~/work/net-2$ ./a.out
[predict your output here #1]
dyoo@dyoo-desktop:~/work/net-2$ ./a.out | cat
[predict your output here #2]
Yes; I was surprised too, but #1 and #2 can produce different output.
At least, this is what I see:
dyoo@dyoo-desktop:~/work/net-2$ ./a.out
hello world
child
parent
dyoo@dyoo-desktop:~/work/net-2$ ./a.out | cat
hello world
child
hello world
parent
The bug here is the interaction between the low-level fork() and the way that buffered output works in C. In the second case, since I'm piping through cat, there's more output buffering going on. The first printf() is buffered and not immediately printed but rather stored somewhere. The fork() duplicates the process. So when both the child and parent processes close, they flush their respective buffers... and we see this unusual output.
Some man pages do talk about this, like the one from SGI IRIX. Linux's man pages, not so much. Ah well.
3 comments:
Hmm. But how can you blame this on C ? Might as well just blame it on the OS and its handling of forked buffers and its insistence that buffers need to be flushed manually... except in fancy hotels...
C is doing exactly what I'm asking it to do, so I can't complain about that.
I guess my unhappiness is more rooted in me not fully understanding the repercussions of my programs. That even a simple program can produce different output like this under a slightly different context just feels unexpected and unsatisfying to me.
Of course, the terminal/file outputs will be the same if the newlines are omitted, but I imagine that only makes you more unhappy... ;)
Post a Comment