Why is output from simple C program adding/deleting newlines unpredictably?
First things first, I'm using cygwin version 1.7.1 on a Windows 7 box. Code was compiled with gcc and ran from a bash prompt. Here goes:
I was researching how fork() and exec() work so I was checking out wikipedia. There I found the following straightforward C code for some fork-on-fork action:
#include <stdio.h> /* printf, stderr, fprintf */
#include <unistd.h> /* _exit, fork */
#include <stdlib.h> /* exit */
#include <errno.h> /* errno */
int main(void)
{
pid_t pid;
/* Output from both the child and the parent process
* will be written to the standard output,
* as they both run at the same time.
*/
pid = fork();
if (pid == -1)
{
fprintf(stderr, "can't fork, error %d\n", errno);
exit(EXIT_FAILURE);
}
if (pid == 0)
{
/* Child process:
* When fork() returns 0, we are in
* the child process.
* Here we count up to ten, one each second.
*/
int j = 0;
for (j = 0; j < 10; j++)
{
printf("child: %d\n", j);
sleep(1);
}
_exit(0); /* Note that we do not use exit() */
}
else
{
/* Parent process:
* When fork() returns a positive number, we are in the parent process
* (the fork return value is the PID of the newly created child process).
* Again we count up to ten.
*/
int i = 0;
for (i = 0; i < 10; i++)
{
printf("parent: %d\n", i);
sleep(1);
}
exit(0);
}
}
Now when I compile and run it a few times, I seem to get unpredictable behavior... sometimes it runs as expected, sometimes it includes extra newline characters to stdout, sometimes it omits newline characters to std out. Here is a sample of the output:
user@HAL10000 ~/c++/sandbox/src
$ gcc fork_and_stuff.c -o fork_and_stuff
user@HAL10000 ~/c++/sandbox/src
$ ./fork_and_stuff.exe
parent: 0child: 0
parent: 1child: 1
parent: 2child: 2
parent: 3child: 3
parent: 4child: 4
parent: 5child: 5
child: 6
parent: 6
child: 7
parent: 7
child: 8
parent: 8
child: 9
parent: 9
user@HAL10000 ~/c++/sandbox/src
$ ./fork_and_stuff.exe
parent: 0
child: 0
parent: 1
child: 1
parent: 2
child: 2
parent: 3
child: 3
parent: 4
child: 4
parent: 5
child: 5
parent: 6
child: 6
parent: 7
child: 7
parent: 8
child: 8
parent: 9
child: 9
user@HAL10000 ~/c++/sandbox/src
$ ./fork_and_stuff.exe
parent: 0child: 0
parent: 1child: 1
parent: 2child: 2
parent: 3child: 3
parent: 4child: 4
child: 5
parent: 5
parent: 6child: 6
parent: 7child: 7
child: 8parent: 8
parent: 9child: 9
T开发者_StackOverflow中文版hat is some spooky looking output. Is my computer haunted? If so, by what? And how might I exorcise it?
I have a feeling that this is caused by flushing behavior. Try calling fflush(stdout);
after each print statement and see whether that changes the results.
For more information, I would recommend reading through the answers to this question (especially this explanation of ISO behavior for buffered and unbuffered streams).
This is normal behvior for fork(). Your code is causing a really nice scheduler interaction. This has, in fact, been used as a low quality RNG.
Calling fflush() might well decrease the probability but will not actually remove the chance of it happening.
Your code should do what you want most of the time if you add usleep(500000); to the top of one side; however this behavior should not be depended on.
The intent of fork() is to create two independent processes. There are standard inter-processing locking mechanisms available to permit your code to work; however using them is almost always an error. There is a reason for the standard UNIX rule of "If a program has nothing surprising to say, it should say nothing." This allows them to run as background processes trivially.
Incidentally, you set up something very like the trick we use to demonstrate the unpredictableness of the scheduler.
Here is one possible way to exorcise the evil spirit that is haunting your computer :) That is, try running the program on Cygwin bash shell or Windows' command prompt. The problem is, the output is not being flushed upon \n
. To flush it, try fflush(stdout)
after each printf
call.
精彩评论