We’ve spent the last several pages on almost microscopic details of process behavior. Rather than continue our descent into the murky depths, we’ll revert to a higher-level view of processes.
Earlier in this chapter, we covered ways of controlling multiple simultaneous jobs within an interactive login session; now we’ll consider multiple process control within shell programs. When two (or more) processes are explicitly programmed to run simultaneously and possibly communicate with each other, we call them coroutines.
This is actually nothing new: a pipeline is an example of coroutines. The shell’s pipeline construct encapsulates a fairly sophisticated set of rules about how processes interact with each other. If we take a closer look at these rules, we’ll be better able to understand other ways of handling coroutines—most of which turn out to be simpler than pipelines.
When you invoke a simple pipeline—say, ls | more—the shell invokes a series of UNIX primitive operations, or system calls. In effect, the shell tells UNIX to do the following things; in case you’re interested, we include in parentheses the actual system call used at each step:
Create two subprocesses, which we’ll call P1 and P2 (the fork system call).
Set up I/O between the processes so that P1’s standard output feeds into P2’s standard input (pipe).
Start /bin/ls in process P1 (exec).
Start /bin/more in process P2 (exec).
Wait for both processes to finish (wait).
You can probably imagine how the above steps ...