Dan
Dan

Reputation: 10786

Fork and wait - how to wait for all grandchildren to finish

I am working on an assignment to build a simple shell, and I'm trying to add a few features that aren't required yet, but I'm running into an issue with pipes.

Once my command is parsed, I fork a process to execute them. This process is a subroutine that will execute the command, if there is only one left, otherwise it will fork. The parent will execute the first command, the child will process the rest. Pipes are set up and work correctly.

My main process then calls wait(), and then outputs the prompt. When I execute a command like ls -la | cat, the prompt is printed before the output from cat.

I tried calling wait() once for each command that should be executed, but the first call works and all successive calls return ECHILD.

How can I force my main thread to wait until all children, including children of children, exit?

Upvotes: 6

Views: 7344

Answers (3)

CharMstr
CharMstr

Reputation: 323

Since you are talking about grandchilds, you are obviously spawning the childs in a cascading manner. Thats a possible way to implement a pipe.

But keep in mind that the returned value from your pipe (the one you get when doing echo $? in your terminal) is the one returned from the right-most command.

This means that you need to spawn childs from right to left in this cascading implementation. You dont want to lose that returned value.

Now assuming we are only talking about builtin commands for the sake of simplicity (no extra calls to fork() and execve() are made), an intersting fact is that in some shells like "zsh", the right-most command is not even forked. We can see that with a simple piped command like:

export stack=OVERFLOW | export overflow=STACK

Using then the command env, we can appreciate the persistance of the overflow=STACK in the environment variables. It shows that the right-most command was not executed in a subshell, whereas export stack=OVERFLOW was.

Note: This is not the case in a shell like "sh".

Now lets use a basic piped command to give a possible logic for this cascading implementation.

cat /dev/random | head

Note: Even though cat /dev/random is supposedly a never ending command, it will stop as soon as the command head is done reading the first line outputed by cat /dev/random. This is because stdin is closed when head is done, and the command cat /dev/random aborts because its writing in a broken pipe.

LOGIC:

  1. The parent process (your shell) sees that there is a pipe to execute. It will then fork two processes. The parent stays your shell, it will wait for the child to return, and store the returned value.

  2. In the context of the first generation child: (trying to execute the right-most command of the pipe) It sees that the command is not the last command, it will fork() again (What i call "cascading implementation"). Now that the fork is done, the parent process is going to execute first of all its task (head -1), it will then close its stdin and stdout, then wait() for its child. This is really important to close firstly stdin and stdout, then call wait(). Closing stdout sends EOF to the parent, if reading on stdin. Closing stdin make sure the grand-children trying to write in the pipe aborts, with a "broken pipe" error.

  3. In the context of the grand-children: It sees that it is the last command of a pipe, it will just execute the command and return its value (it closes stdin and stdout).

Upvotes: 1

Art
Art

Reputation: 20392

You can't. Either make your child process wait for its children and don't exit until they've all been waited for or fork all the children from the same process.

Upvotes: 8

Aaron Digulla
Aaron Digulla

Reputation: 328546

See this answer how to wait() for child processes: How to wait until all child processes called by fork() complete?

There is no way to wait for a grandchild; you need to implement the wait logic in each process. That way, each child will only exit after all it's children have exited (and that will then include all grandchildren recusively).

Upvotes: 6

Related Questions