Using FIFOs in Linux

I ran into an interesting "feature" when using two FIFO's in Linux for bi-directional communication between two processes. Basically, I kept encountering a deadlock no matter what I did, and I couldn't figure out why.

Here's the scenario: You have two separate processes (not necessarily parent/child) and two separate FIFO's. Although a FIFO can be used for bi-directional communication, each one will be set up to be uni-directional: Process #1 will write to FIFO #1 and read from FIFO #2, while Process #2 will write to FIFO #2 and read from FIFO #1. Doing things this way allows Process #1 to pass data to Process #2 via FIFO #1, and Process #2 can pass a result back to Process #1 via FIFO #2. This provides an easy way to keep both processes synchronized, because each process will block when reading the FIFO streams.

Each process blocks when opening both FIFOs as well (during initialization, for example) until both a read and a writer are present (which is normal). Once they are opened, Process #1 writes some data (a number, for example) to FIFO #1. Process #2 should receive the data and return a value via FIFO #2. However, no matter what I tried, the data never reached Process #2, and both processes were in deadlock. But if only a single FIFO was used (where data was passed from Process #1 to Process #2 only), everything worked fine!

If you're now asking, "Just what the hell is a FIFO?", then stop reading here. If you want to know what the problem was and the solution, keep reading.

This silly problem stumped me for some hours. The internets didn't offer much help - apparently other people don't have this type of problem.

Here is the first example using a single FIFO that actually works.

C:
  1. // Server, single FIFO
  2. int main(char argc, char **argv)
  3. {
  4. FILE * fifo = fopen("/tmp/cvalidout", "w");
  5. int s = 123;
  6. fwrite( (void*)&s, sizeof(s), 1, fifo);
  7. fclose(fifo);
  8. }

C:
  1. // Client, single FIFO
  2. int main(char argc, char **argv)
  3. {
  4. FILE * fifo = fopen("/tmp/cvalidout", "r");
  5. int s;
  6. fread( (void*)&s, sizeof(s), 1, fifo);
  7. printf("Got value: %d\n", s);
  8. fclose(fifo);
  9. }

The above two examples work fine. However using two FIFOs for bi-directional communication (as in the examples below) does NOT work. Why?

C:
  1. // Server, two FIFOs
  2. int main(char argc, char **argv)
  3. {
  4. FILE * fifoin = fopen("/tmp/cvalidin", "r");
  5. FILE * fifoout = fopen("/tmp/cvalidout", "w");
  6. int s = 123;
  7. fwrite( (void*)&s, sizeof(s), 1, fifoout);
  8. fread( (void*)&s, sizeof(s), 1, fifoin);
  9. printf("Got answer from client: %d\n", s);
  10. fclose(fifoin);
  11. fclose(fifoout);
  12. }

C:
  1. // Client, two FIFOs
  2. int main(char argc, char **argv)
  3. {
  4. FILE * fifoout = fopen("/tmp/cvalidin", "w");
  5. FILE * fifoin = fopen("/tmp/cvalidout", "r");
  6. int s;
  7. fread( (void*)&s, sizeof(s), 1, fifoin);
  8. printf("Got value: %d\n", s);
  9. s = 456;
  10. fwrite( (void*)&s, sizeof(s), 1, fifoout);
  11. fclose(fifoin);
  12. fclose(fifoout);
  13. }

The solution is this:

I was using fread() and fwrite() calls in these examples. The fwrite() call uses some kind of buffering. So after every fwrite() call, you need to use the fflush() call so that the written data is actually committed. That's all! To make the server/client programs work, insert a fflush() after the fwrite() calls.

The reason that the first sample program I wrote works is because the server program writes the value to the pipe (which does not block) and then promptly ends. When the process ends, the buffer is flushed automatically - which is what I didn't realize, and which is why I took so long to figure out what the hell was going on. Geez.

Anyway, I hope someone else that runs into this same sillyness can find this post and save themselves a little bit of time!

Leave a Comment

Trackback: http://www.heavygravity.com/2006/08/26/using-fifos-in-linux/trackback/



Everything here copyright rob russell, heavygravity.com.