2

My question is about how to control the process execution with regards to pipes, and specifically implementation of wait / waitpid function.

When I create a pipe for the following command ls | head -3, I do the following:

  • I create the pipe, fork the process creating a child
  • For the child, I call dup2 for stdin, I execute the head -3 command, closing output side of pipe in the child
  • For the parent, I call dup2 for stdout, I execute the ls command, closing the input side of pipe in the parent

My question: based on this discussion, I do need to wait for the child to complete the execution, i.e. the execution of head -3. But how/where can I implement the waitpid function in such a way that it won't conflict with the close[] command?

Based on this great text, the description reads:

If the parent wants to receive data from the child, it should close fd1, and the child should close fd0. If the parent wants to send data to the child, it should close fd0, and the child should close fd1. Since descriptors are shared between the parent and child, we should always be sure to close the end of pipe we aren't concerned with. On a technical note, the EOF will never be returned if the unnecessary ends of the pipe are not explicitly closed.

So the child will have to wait for the parent to complete the pipe, before execution.

I have also seen examples where two forks are made for one process with pipe. Can it be the purpose of avoiding zombie processes, just like the description in my copy of APUE ch.8?

Consider the following code implementation:

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

    int main()
    {
    int pid, status;

    int fd[2];

    char *com1[2];
    char *com2[3];

    com1[0] = "ls";
    com1[1] = NULL;

    com2[0] = "head";
    com2[1] = "-3";
    com2[2] = NULL;

    pipe(fd);

    if((pid = fork()) == -1)
    {
        printf("fork error");
        exit(1);
    }

    if(pid == 0)
    {
        /* Child process closes up output side of pipe */
        dup2(fd[0], 0);
        close(fd[1]);
        execvp(com2[0], com2);
    }

    else
    {

        /* if(waitpid(0, WIFEXITED(&status), 0) != pid)
        {
            printf("wait error");
        }
        is this even needed here? */

        /* Parent process closes up input side of pipe */
        dup2(fd[1], 1);
        close(fd[0]);
        execvp(com1[0], com1);

    }

    exit(0);

}

I must admit that I have looked through many similar questions here on StackExchange. Almost all of them are of specific content however. What I am looking for are explanations of principles and combinations of the functions. Thank you for your time!

Community
  • 1
  • 1
Pavoo
  • 75
  • 2
  • 6
  • 3
    If you want the parent process to continue executing, you need to create *two* child processes: One for the `ls` command and one for the `head` command. And no the child (in your program) does not have to wait for the parent, if there's nothing to currently read from the pipe all non-blocking reading (which is the default) will simply block until there is data to read or some error occurs. – Some programmer dude Jun 18 '16 at 09:18
  • 1
    @JoachimPileborg: "All non-blocking reading will block"? – Kerrek SB Jun 18 '16 at 09:29
  • Ehm... All non non-blocking reads will block I mean... :) – Some programmer dude Jun 18 '16 at 09:42

1 Answers1

0

You can't both want to wait for a child and exec. So you need to have two child. There is two common way to do this, either have a parent process that creates two direct children and can/must then wait for both; or have a child that itself creates the second one, so that the parent only have to wait for one process termination.

Option 1 (inherited pipe)

pipe(pi);
pid1 = fork();
if (pid1==0) { // child1
  // make redirections
  exec("head"...);
}
pid2 = fork();
if (pid2==0) { // child2
  // make redirections
  exec("ls"...);
}
// parent process
// close unuseful pipe
waitpid(pid1...);
waitpid(pid2...);

Option 2 (pipe is only visible to the concerned processes)

pid1 = fork();
if (pid1==0) { // child
  pipe(pi);
  pid2 = fork();
  if (pid2==0) { // gran child
    // make redirections
    exec("ls"...);
  }
  // make redirections
  exec("head"...);
}
// parent process
waitpid(pid1...);
Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
  • "have a child that itself creates the second one" who will wait for the second one then? It's the same situation as with one child. – n. m. could be an AI Jun 18 '16 at 09:58
  • Two children is not the same situation as one child, obviously (the main advantage is that the parent now can wait...). All of this should be included in a process group, easier to manage, but it's another story... – Jean-Baptiste Yunès Jun 18 '16 at 10:07
  • In your second snippet you only have one waitpid. Where does the second one go? – n. m. could be an AI Jun 18 '16 at 10:26
  • No need, the terminated child will be inherited by `initd` who will `wait` for it. – Jean-Baptiste Yunès Jun 19 '16 at 17:52
  • So you don't care whether the child terminated successfully or not. Why run it in the first place, then? – n. m. could be an AI Jun 19 '16 at 18:04
  • OP wants to launch `ls | head` and wait for its termination so he needs to wait for at least one of the two processes... As `ls | head` can be considered as terminated when `head`terminates, then he, at least, needs to wait for it (waiting for `ls` termination is not really important in this case). – Jean-Baptiste Yunès Jun 19 '16 at 18:12
  • It is unwise to ignore child exit status, it may indicate a problem that is hard to see otherwise. Maybe in a toy program it's OK but not in a production program. What if `ls` or `head` could not start for some reason (process table is full or whatever)? The user won't see any output and falsely assume there are no files. – n. m. could be an AI Jun 19 '16 at 18:21