Doh! Stupid Programming Mistakes <humor>

Jacob Fugal lukfugl at gmail.com
Thu Oct 19 11:55:26 MDT 2006


On 10/19/06, Alex Esplin <alex.esplin at gmail.com> wrote:
> On 10/19/06, Corey Edwards <tensai at zmonkey.org> wrote:
> > I guess you forked off 10! (3,628,800) processes. We like to call that a
> > fork bomb.
>
> Eh?  Somebody wanna enlighten a confused young whippersnapper that has
> no clue what is going on here but would like to learn something?

The for loop from Steve's original email:

   for(x = 0; x < 10; x++){
       if(fork() > 0){
               MPI_Comm_rank(MPI_COMM_WORLD, &myrank); /* Get my rank
        */
               MPI_Comm_size(MPI_COMM_WORLD, &size);   /* Get the
total number of
processors */
               printf("Processor %d of %d: Running!\n", myrank, size);
       }
   }

The fork command duplicates the process and both the original process
and the child process continue execution from the point of the fork.
The only visible difference to the two processes is the return value
from fork. The parent process gets a zero back, while the child
process gets its own process ID back (I think that's right anyways,
been a while since I did it). So the first time through the loop, the
parent process forks off child 1. But both processes continue with the
full loop. So both parent and child 1 go through the second iteration,
forking off child 2 and child 1.1, respectively. Then the third time
through the loop, each of those four processes fork off another child:
child 3, child 1.2, child 2.1 and child 1.1.1. So on and so forth for
10 iterations. In effect you double the number of processes every
iteration. With 10 iterations you end up with over 2^10 = 1024
processes (not as bad the 10! that Corey predicted, but still pretty
bad -- and maybe I'm counting wrong and it does follow a factorial
progression).

Corey's suggested solution is also a little off. His parent flag does
prevent the deep forking, but there's another subtle hole hidden in
there. Symmetry would suggest that MPI_Init and MPI_Finalize should be
called in pairs. But in this code, MPI_Init is called only once by the
parent process, but MPI_Finalize is called by each forked process!
Now, if MPI_Finalize turns into a NOOP when called multiple times,
this doesn't hurt -- it's just redundant. But if MPI_Finalize just
assumes it's going to be called only once and does operations that
will be dangerous when called multiple times (e.g. try and free
memory, or update rows in a database), this could cause problems.

Generally, what's done with forking is that each child process does
exactly what is required of it, then exits. I would rewrite Steve's
code as:

#include <stdio.h>
#include <mpi.h>

void main (int argc, char *argv[])
{
   int myrank, size,x;

   MPI_Init(&argc, &argv);                 /* Initialize MPI       */

   for(x = 0; x < 10; x++){
       if(fork() > 0){
               MPI_Comm_rank(MPI_COMM_WORLD, &myrank); /* Get my rank
        */
               MPI_Comm_size(MPI_COMM_WORLD, &size);   /* Get the
total number of
processors */
               printf("Processor %d of %d: Running!\n", myrank, size);
               return;
       }
   }
   MPI_Finalize();                         /* Terminate MPI        */
}

There's no need for a parent flag because each child terminates
immediately after doing its chores. And only the parent will ever make
it all the way through the for loop and execute the MPI_Finalize call.

Jacob Fugal



More information about the PLUG mailing list