Class 12: Named Pipes (i.e. FIFOS)


Reading
None

Homework
None


The motivation for named pipes (FIFOS)
If you start with the coin toss program as we know it from previous classes and try to add the window plotting functionality of pp, you quickly discover a problem (as indeed we did with our in-class demo): the coin toss program can't continue because the pp plotting function never returns. So the whole concept of a loop in which we can ask for more and more trials is out the window. This led us to an approach in which, instead of calling the pp library function we used (due to Addison's vision) the system function like this:
system("pp &");
to spawn a pp-process and bring control immediately back to our coin toss program. The only problem here is that we have no mechanism for sending pp the points we want plotted. We could create a file tmpfile, write the points to tmpfile and then invoke pp with:
system("pp < tmpfile &");
This approach is ugly, however, in a few ways. First of all, writing data to disk is very slow, so you're introducing a completely unnecessary and very serious bottleneck into the system. Second, you have this file sitting around and, unfortunately, it's very difficult to tell whether and when it can be removed, and whose job it would be to do the removing.

Named pipes (or FIFOS) offer us a better alternative. What you create is something that looks like a file, and in many ways acts like a file, but which never actually involves storing the data you write in the filesystem. It's like a chat room - an address that the writer and reader agree to meet at to exchange data. Data isn't stored, even temporarily, in a FIFO. It's just the connection between reader and writer. This makes it fast, and this also means it enforces a bit of necessary synchronization: the writer has to wait to write until there's someone there to read, and the reader sits there and waits until there's something there to be read. Now our setup is like this:

1) create FIFO f
2) system(pp < f &);
3) write points to f
4) remove f
Some salient points: 1) we can't actually start part 3 until the pp process is spawned and connected to pipe f. Why? Because you can't write without someone reading on the other end! 2) you can remove a FIFO with rm, but this only disassociates the name from the actual pipe. The pipe lives on until nobody has it open any more. 3) We can safely rm the FIFO at step 4 even if the pp-process still had some data left to read, because the pipe wouldn't die, just its name would, and since pp has opened the pipe already its name isn't needed. In actual code these steps look like this:

      // make a named pipe in /tmp
      string fifoname = "/tmp/cthPipe";
      mkfifo( fifoname.c_str() , S_IRUSR | S_IWUSR);      

      // use the Addison trick to spawn a pp process and
      // return control back to our program immediately
      // NOTE: this requires that pp is in the current directory
      system(("pp < " + fifoname + " &").c_str());
      
      // open ofstream to the pipe, write to it, close it
      ofstream fout(fifoname.c_str());
      for(int i = 0; i < X.size(); ++i)
        fout << X[i] << ' ' << Y[i] << endl;
      fout.close();
      
      // remove the named pipe.  Note: the pipe gets removed
      // as an entry in the filesystem, but lives on namelss
      // as long as the pp-process which refernces it is around
      system(("/usr/bin/rm " + fifoname).c_str());
	
A complete solution with named pipes
A complete solution to the coin toss problem is given by cth.cpp and Makefile. One benefit of this approach to using pp versus the library approach is that our program doesn't need to link to the X, OpenGL or Glut libraries. Thus, the Makefile is pretty straightforward. On the otherhand, our code needs to have location of pp hardcoded in our, as in this case, must assume that pp is found via the PATH environment variable in the shell system spawns.


Christopher W Brown
Last modified: Tue Feb 8 13:57:23 EST 2005