Forking without exec()

A Unix-like system may create the child process using fork(), which then does not make a subsequent exec() call. The result of this is that the parent and child processes run the same executable. The child may communicate with the parent using pipes. One example of a system which does this is the email software program Exim (www.exim.org). In addition to forking without exec(), it can also re-exec() itself to regain dropped root privileges.

The issues that such systems encounter when porting to P.I.P.S. fall into two categories:
  • Little or no state data passed to child

  • A lot of data passed to child

Little or no state data passed to child

The first issue is where there is a little/no data passed to the child process on the fork() operation. Many examples of this exist in pre/post forking of listening sockets in TCP server applications, for example, in the MPM pre-fork module of the Apache server. More details are available at http://httpd.apache.org/docs/2.2/mod/prefork.html.

This can be resolved by using the posix_spawn() operation, and passing any data using the argv parameters or environment variables. For more information about the posix_spawn() operation, see http://www.opengroup.org/. Note that some argv parameters must be used to distinguish the behaviour of the parent process from the subsequent behaviour of the child when the main() function is called; the behaviour of the child cannot be identical to the parent.

The subsequent sections provide the following information:
  • Parent process forking example

  • P.I.P.S. equivalent

Parent process forking example

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define NUM_OF_PROCS 5

int main(int argc, char *argv[])
{
  pid_t Processpid[NUM_OF_PROCS];
     
  int I;
  
  for(I=1;i<NUM_OF_PROCS;i++)
  {
     Processpid[i] = fork();
    
     if(Processpid[i] == 0)
     {
        printf("\r\n Child Process Inst[%d] running ***\r\n",I);

        //Terminate child process.5
        exit(0);
     }
     else
     {
        //Wait for the child process to terminate before forking the next one.
        waitpid(Processpid[i],NULL,0);
     
        printf("\r\n*** Child int[%d] process finished ***\r\n",I);
     }
  }
   
  return EXIT_SUCCESS;
}

P.I.P.S. equivalent

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <spawn.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NUM_OF_PROCS 5

int main(int argc, char *argv[])
{
   if(argc != 2)
   {
      printf("\r\n One parameter is needed.  \r\n");
      
      return EXIT_FAILURE;
   }
   else
   {
      int argvAsInt = atoi(argv[1]);
      
      if(argvAsInt > NUM_OF_PROCS)
      {
         printf("\r\n parameter[%d] one is out of range  \r\n",argvAsInt);
       
         return EXIT_FAILURE;
      }
      else
      {      
         if(argvAsInt == 0)
         {
            //parent process.
            pid_t Processpid[NUM_OF_PROCS];
   
            //executable points to the compiled version of this source.
            char execFileName[] = "/root/PortDoc/Example3_c/Symbian/ParentProg";
            int RetVal;
            int I;
            char iAsString[2];
            char* spawnedArgs[3];
            spawnedArgs[0] = argv[0];
            spawnedArgs[2] = NULL;
    
            for(I=1; i<NUM_OF_PROCS;i++)
            {
               //store I as a string.
               bzero(iAsString,sizeof(iAsString));
               iAsString[0] = 0x30+I;
       
               spawnedArgs[1] = iAsString;
               
               RetVal= posix_spawn(&Processpid[i],execFileName,NULL,NULL,spawnedArgs,NULL);
   
               //wait for chid process to terminate before spawning the next.
              (void)waitpid(Processpid[i],NULL,0);
               printf("\r\n*** Child process finished ***\r\n");
            }
         }
         else
         {
            //child process
            printf("\r\n Child Process Inst[%d] running ***\r\n",argvAsInt);

            //Terminate child process.
            exit(0);
         }
      }
   }
   return EXIT_SUCCESS;
}

A lot of data passed to child

The second issue is where there is too much data to be passed across to the child process using the posix_spawn() call. A common work-around used in systems where fork() is not available is to use POSIX threads, or Pthreads. These Pthreads will execute in the same process and share their memory space, that is, they can share the same data objects. One critical difference between using Pthreads and fork() is that fork() creates copies of the parent's data objects in the child. The copied data objects can then be modified independently by both processes. However, when using Pthreads such data objects are shared and extra care, such as the use of mutexes and semaphores, is required when accessing them if their values can change.