Parent and Child IPC Using a Single Pipe

In this scenario, the parent and child processes communicate using a single pipe. The following pseudo code details the mechanism used by code in the parent process in a Unix-like system.
       //Call pipe() to create the pipe.
//Call Fork().
if (child process)
{
   //Child Process.
   //duplicate (via a dup2() call) the read/write end of the pipe using  
   //prior agreed file descriptor numbers which will subsequently be used by the   //child process following the exec() call.
   //Close the original file descriptors.
   //Call exec() to replace the code image in the child process.
}
else
{
   //Parent process
   //Do Whatever
}
      

Instead of using the fork() / exec() as described above, POSIX libraries including P.I.P.S. provide the popen() function as defined in stdio.h . Rather than using a pipe for the IPC mechanism the parent process communicates with the child using the stream returned by the popen() call, while the child process input or output is using the stdin() / stdout() streams.

However if the parent process requires a file descriptor it can be obtained by using the fileno() API call. For more information about the popen() function, see http:\\www.opengroup.org .

Parent process fork() and exec() functions

The following code shows the use of a pipe and subsequent fork() / exec() calls to create a Pipe for IPC. The example shown is the child process writing data to the pipe, and the parent process receiving the data.

      //Child Process File Descriptors for the pipe. These should be defined in a common header 
//file used to compile both the parent and child process's executable.
//However #define is here for clarity.
#define WRITE_FD  104
int main(int argc, char *argv[])
{
  int pipeEnds[2]; //Pipe file descriptors [0] is for read [1] is for write
  
  //create the pipe
  if(pipe(pipeEnds) != 0)
  {
     //Pipe creation error
     return EXIT_FAILURE;
  }
  else
  {
     pid_t Childpid = fork();
     
     if(Childpid == 0)
     {
         //close the redundant read FD obtained from the parent process
         (void)close(pipeEnds[0]);
    
         //duplicate the file descriptor for use following exec().
         if(dup2(pipeEnds[1], WRITE_FD) == -1)
         {
            printf("dup2 error\n");
            return EXIT_FAILURE;
         }
         //close the redundant write FD obtained from the parent process
         (void)close(pipeEnds[1]);
    
         //exec() to replace the child process executable
         char execFileName[] = "/root/PortDoc/Example1_c/Posix/Child/ChildProg";
         execl(execFileName,NULL);
      }
      else
      {
         //Parent process. This reads from the pipe. Therefore close the write end of the
         //pipe.
         close(pipeEnds[1]);
    
         //declare receive buffer, and clear its contents
         char RxBuffer[100];
         memset(RxBuffer,0,sizeof(RxBuffer));
    
         //Wait for data from the child process. Child sends a string.
         read(pipeEnds[0],RxBuffer,sizeof(RxBuffer));
    
         printf(RxBuffer);

         //close the Read end of the pipe
         close(pipeEnds[0]);
    
         //Wait for the child process to terminate
         waitpid(Childpid,NULL,0);
      }
   }
   return EXIT_SUCCESS;
}
     

Child process created using fork() or exec() functions

The following code shows an example of the child process source which will be executed following the exec() .

      //Child Process File Descriptors for the pipe. These should be defined in a common header 
//file used to compile both the parent and child process's executable. Shown here for
//clarity.
#define WRITE_FD  104

int main(void)
{
   char TxMsg[] = "Hello Parent\n";
    
   //Send the message to the parent
   write(WRITE_FD,TxMsg,sizeof(TxMsg));
    
   //close the File Descriptor
   close(WRITE_FD);
   return EXIT_SUCCESS;
}
     

Parent process P.I.P.S. example for popen() function

The following code shows how the above code can be modified to use popen() , rather than the fork() / exec() combination.

      int main(int argc, char *argv[])
{
   //Create child process using popen(). Child process writes to the Parent therefore "r" 
   //parameter.
   FILE* ChildProcessStream = popen("/root/PortDoc/Example1_c/Symbian/Child/ChildProg","r");
          
   if(ChildProcessStream == NULL)
   {
      printf("\n Failure to create child process with popen()\n");
      return EXIT_FAILURE;
   }
   else
   {
      //Use a file descriptor rather than a stream
      int ChildProcessFD = fileno(ChildProcessStream);

      //Create a receive buffer, and zero contents before receiving.
      char RxBuffer[100];
      memset(RxBuffer,0,sizeof(RxBuffer));
      
      //Wait for data from the child process. Child sends a string.
      int nbytes = read(ChildProcessFD,RxBuffer,sizeof(RxBuffer));

      //printf is slightly different from the POSIX example to verify that the data output to the 
      //screen is done by the parent process.
      printf("\n Message Received by Parent=%s",RxBuffer);
      
      //Wait for Child Process to complete
      pclose(ChildProcessStream);
   }
   return EXIT_SUCCESS;
}
     

Child process P.I.P.S. example for popen() function

The following code shows how the child process source can be modified when it is executed using popen() . Note that rather than using a file descriptor for the communication via the pipe the stdin() or the stdout streams are directed to the parent process, as defined in the usage of the popen() call.

      int main(void)
{
    //Child process created by popen() so that its stdout is streamed to the parent process
    char TxMsg[] = "Hello Parent\n";
    
    //Send the message to the parent
    printf(TxMsg);
    
    return EXIT_SUCCESS;
}