The program creates two processes, connected by a circular linkage of pipes. A single integer is circulated through these pipes between the two processes. The integer is initially zero. Each process increments this circulating integer before passing it on, and each process terminates as soon as the circulating integer has been incremented to a value greater than 4. One process is designated the parent, and the other is designated the child; each writes out its identity when it receives the circulating integer. This behavior means that there will be a total of 5 lines of output from parent and child (after the initial diagnostic), with one printing 3 and the other 2 lines of output.
main() { int p1[2], p2[2], pid, cc, buf[1]; pipe(p1); pipe(p2); buf[0] = 0; cc = write( p1[1], buf, 4 ); cc = write( 1, "pre-fork\n", 9 ); cobegin while (buf[0] < 4) { cc = read( p2[0], buf, 4 ); cc = write( 1, "parent\n", 7 ); buf[0]++; cc = write( p1[1], buf, 4 ); }; while (buf[0] < 4) { cc = read( p1[0], buf, 4 ); cc = write( 1, "child\n", 6 ); buf[0]++; cc = write( p2[1], buf, 4 ); } coend; }
The output is not necessarily different. The code for printf behaves differently when directed to an interactive terminal or to a non-interactive file or device. When directed to a terminal, printf flushes its buffers after every output of a newline, but when directed to a non-interactive file, buffers are only flushed when they fill or are explicitly flushed. The following two example runs illustrate this difference:% t1 pre-fork child parent child parent child % t1 | cat pre-fork child child child pre-fork parent parentIn the second example, the un-flushed buffer was copied and given to both processes when the fork was done, and then each added its own output. The buffers were only flushed when the process terminated, and since the parent waited for the child to terminate, the child's buffer was output first.
I expect a deadlock. The reason is that streams are buffered until some event causes the buffer to be flushed. The basic stream operations putc and getc are macros, and you can inspect the code involved in doing the buffer flushing in the include file stdio.h.Merely opening the streams poses problems, because some versions of fdopen may do a preliminary read to fill the buffer, causing the deadlock to emerge even before any explicit reads or writes have been performed. Thus, only the parent's output stream should be opened prior to the fork, and the child should open its streams after the fork.
With this precaution, the deadlock emerges after the fork, and this should be resolved with the insertion of an fflush operation after each fprintf. Unfortunately, even this does not solve the problem, and most UNIX implementors don't provide workable solutions, but instead suggest that programs that use pipes should use read and write instead of the higher level stream primitives.