Search code examples
cforkdup2

Piping output to forks in C


I have a multiple processes in progs and I wish to pipe the output from one process to another sequentially. I believe I have already linked the stdin of a process to the read end of a previous process, and linked the stdout to the write end of the pipe. I still am not seeing an output. Am I missing something with the links here?

  int pipeFds[numProg][2];
  
  for (int i = 0; i < numProg; i++) {
    if (pipe(pipeFds[i]) != 0) { // create pipe for each process
      printf("Failed to create pipe!");
      exit(1);
    }
    child_pid = fork();
    if (child_pid == -1) {
      printf("Error while creating fork!");
      exit(1);
    } 
    if (child_pid == 0) {
      if (i == 0) { 
        close(pipeFds[i][READ_END]); // close STDIN first process since it does not read
      } else { 
        // change stdin to read end of pipe for intermediary processes
        close(pipeFds[i]);
        dup2(pipeFds[i - 1][READ_END], STDIN_FILENO);
      }
      dup2(pipeFds[i][WRITE_END], STDOUT_FILENO); // change stdout to write end of pipe
      execvp(progs[i][0], (char *const * )progs[i]);
    } else {
      // parent process stuff
    }
  }
  
  // Close pipes except last pipe for EOF
  for (int i = 0; i < numProg - 1; i++) {
    close(pipeFds[i][READ_END]);
    close(pipeFds[i][WRITE_END]);
  }


Solution

  • Remember you need to close all pipes, in each process, for it to work.

    Example:

    If numProg=2 you create 2 pipes and 2 child processes. Adding the parent, there a 3 processes running, and in each of them you eventually need to close all pipes.

    For child_pid==0 you close the [READ_END] but never the [WRITE_END].

    For child_pid==1 you do a close(pipeFds[1]). You need to specify [READ_END] and [WRITE_END].

    Then each child process exits via execvp which, however, may return control if it fails. From the man page:

    The exec() family of functions replaces the current process image with a new process image. .. The exec() functions only return if an error has occurred.

    So you may want to add a _exit(0); after execvp to ensure each child process exits properly even if execvp fails.

    The parent process closes all pipes but the last. So in the example of NumProg=2, [READ_END] and [WRITE_END] of pipeFd[1] both are never closed.

    Lastly, the parent process should wait for all child processes to close (using while(wait(NULL) != -1);) otherwise you may end up with zombie or orphan processes.