3

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

That is some spooky looking output. Is my computer haunted? If so, by what? And how might I exorcise it?

Jimmy
  • 4,419
  • 6
  • 21
  • 30

3 Answers3

3

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).

Community
  • 1
  • 1
Chris Frederick
  • 5,482
  • 3
  • 36
  • 44
  • See also 2.5.1 in POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_05_01 – R.. GitHub STOP HELPING ICE Jul 27 '11 at 23:27
  • So adding `fflush(stdout);` after the print statements did not change the behavior... even through the string does have a newline and I politely ask it to flush. – Jimmy Jul 27 '11 at 23:57
0

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.

vpit3833
  • 7,817
  • 2
  • 25
  • 25
  • +1 for linking to an article that also uses the word 'haunt'. I believe I am running the cygwin bash shell on a windows command prompt window (although I have it all tricked out to be super-big with old school raster fonts using white text on a light gray background, makes me so happy). Anyway when I `echo $TERM` it says `cygwin`. – Jimmy Jul 28 '11 at 00:02
0

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.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • I fear you may be correct; fflush() apears to have no effect. This is not necessarily helping my understanding of fork() as I have never had any problem with input/output before with this setup... Also, I wonder if it is just a flushing issue, because if you look at the very first output the program gives, newlines are actually being lost and not just delayed (i.e. by my count there are only 16 newlines, not the 20 that should be expected). – Jimmy Jul 28 '11 at 00:10
  • You called _exit() without calling fflush() beforehand. That is allowed to lose some output. – Joshua Jul 28 '11 at 15:08