Originally Published: Thursday, 23 August 2001 Author: Subhasish Ghosh
Published to: develop_articles/Development Articles Page: 5/5 - [Printable]

Understanding Linux Kernel Inter-process Communication: Pipes, FIFO & IPC (Part 1)

In this article, part one of a two part article, the prolific and talented Subhasish returns to give Linux.com readers another trip into understanding Linux kernel behavoir and programming. There's a lot of information covered here for free, so hang up your hat and have fun. Part 2 of Understanding Linux Kernel Inter-process Communication will be published tomorrow.

  << Page 5 of 5  

At this point, all readers should notice a very important thing: That is the asymmetry between the use of O_NONBLOCK with O_RDONLY and O_WRONLY. A non-blocking open for writing fails if no process has the pipe open for reading. But a non-blocking read doesn't fail. So, now that we have seen what a FIFO is, what it does, how it operates and the associated functions, let's sum up this section by getting some code up and running.

A few things that need to be mentioned here are: Using the O_NONBLOCK mode affects how read() and write() calls behave on FIFOs. A read on an empty blocking FIFO will wait until some data can be read. Conversely, a read on a non-blocking FIFO with no data will return 0 bytes. A write on a full blocking FIFO will wait until the data can be written. A write on a FIFO that can't accept all of the bytes being written will, in most cases, write part of the data if the request is more than PIPE_BUF bytes. Another very important point to note here is the size of a FIFO. There is a "system-imposed" limit on how much data can be within a FIFO at a given instant of time. This is the #define PIPE_BUF which is usually found in limits.h. On Linux systems, this is commonly 4096 bytes. On other UNIX systems this value may be different. To illustrate how unrelated processes can communicate using named pipes, we create two programs ghosh1.c and ghosh2.c, where the first program is the "producer", cause this creates the pipe, if required, then writes data to it as quickly as possible; the second program is the "consumer". It reads and discards data from the FIFO. So, get moving, and create these two files as given below.

ghosh1.c : The "producer":

/* Inter-process Communication with FIFOs:
Note: For illustration purposes, we don't mind what the data is, so we don't bother to initialize buffer.

By: Subhasish Ghosh
Date: August 17th 2001
Place: Calcutta, WB, India
E-mail:
subhasish_ghosh@linuxmail.org
*/

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/subhasish"
#define BUFFER_SIZE PIPE_BUF
#define TEN_MEG (1024 * 1024 * 10)

int main()
{

int pipe_fd;
int res;
int open_mode = O_WRONLY;
int bytes_sent = 0;
char buffer[BUFFER_SIZE + 1];

if (access(FIFO_NAME, F_OK) = = -1)
{

res = mkfifo(FIFO_NAME, 0777);
if (res != 0)
{
fprintf (stderr, "Could not create fifo %s\n", FIFO_NAME);
exit (EXIT_FAILURE);
}

}

printf ("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf ("Process %d result %d\n", getpid(), pipe_fd);

if (pipe_fd != -1)
{
while (bytes_sent < TEN_MEG)
{
res = write (pipe_fd, buffer, BUFFER_SIZE);
if (res = = -1)
{
fprintf (stderr, "Write error on page\n");
exit (EXIT_FAILURE);
}
bytes_sent += res;
}
(void)close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}

printf ("Process %d finished\n", getpid());
exit (EXIT_SUCCESS);
}

ghosh2.c : The "consumer":

/* The consumer program for program ghosh1:

By: Subhasish Ghosh
Date: August 17th 2001
Place: Calcutta, WB, India
Email:
subhasish_ghosh@linuxmail.org
*/

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/subhasish"
#define BUFFER_SIZE PIPE_BUF

int main()
{

int pipe_fd;
int res;
int open_mode = O_RDONLY;
char buffer[BUFFER_SIZE + 1];
int bytes_read = 0;

memset (buffer, '\0', sizeof(buffer));

printf ("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, open_mode);
printf ("Process %d result %d\n", getpid(), pipe_fd);

if (pipe_fd != -1)
{
do
{
res = read(pipe_fd, buffer, BUFFER_SIZE);
bytes_read += res;
} while (res > 0);
(void) close(pipe_fd);
}
else
{
exit (EXIT_FAILURE);
}

printf ("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit (EXIT_SUCCESS);
}

After creating the files, save them, compile as usual, and then all we need to do is to use the "time" command to time the reader. Thus both the programs run at the same time. We get an output similar to this:

# ./ghosh1 &
[1] 380
Process 380 opening FIFO O_WRONLY

# time ./ghosh2
Process 382 opening FIFO O_RDONLY
Process 380 result 3
Process 382 result 3
Process 380 finished
Process 382 finished, 10485760 bytes read

real 0m0.049s
user 0m0.010s
sys 0m0.040s
[1]+ Done ghosh1

As you might notice, both programs use the FIFO in blocking mode. We start ghosh1 (the writer/producer) first, which blocks, waiting for some reader to open the FIFO. When ghosh2 (the consumer) starts, the writer is then unblocked and starts writing data to the pipe. At the same time, the reader starts reading data from the pipe. This simple pair of programs illustrates the immense potential and beauty of FIFOs.

Okay, now I have one question for all the readers reading this article. It is simple, but I am asking this question so that everyone out there can relate to different facts relating to a FIFO. My question is: Can anyone tell me, when a process opens a FIFO, what role does VFS (Virtual Filesystem) play at that time? The answer is: When a process opens a FIFO, the VFS performs the same operations as it does for device files. The inode object associated with the opened FIFO is initialized by a filesystem-dependent read_inode superblock method. This method always checks whether the inode on disk represents a FIFO. The code snippet maybe represented as below:

if ( inode-> i_mode & 00170000) = = S_IFIFO)
init_fifo (inode);

The init_fifo() function sets the i_op field of the inode object to the address of the fifo_inode_operations table. The function also initializes to 0 all the fields of the pipe_inode_info data structure stored inside the inode object (for pipe_inode_info data structure, please refer to the section earlier where I explained the structure while explaining pipes). I also did mention while explaining pipes the peculiar behavior of the fields of the pipe_inode_info data structure, that they act as "flags" when dealing with pipes, and "counters" when dealing with FIFOs. The code snippet mentioned above that sets the "counter-behavior" of the fields within the pipe_inode_info data structure is the justification of what I had mentioned earlier. This is the beauty of the Linux Kernel, where different entities play different roles when the time comes for them to play their roles. And thus it takes a lot of passion and hard work on behalf of Kernel hackers to identify when and why they act in such a manner. Everything we have covered here in this section on FIFOs is nearly nothing compared to all that really exists. Anyway, I really hope everyone had a lot of fun reading it (cause I had a lot of fun writing it!). So, after dealing with pipe and FIFO, it's high time that we look into the different System V IPC facilities available. So, let's move on to the next section and have some more fun! Hmmmm...

About the Author: My name is Subhasish Ghosh. I'm 20 years old, currently a computer-systems engineering student in India; a Microsoft Certified Professional (MCP), MCSD, MCP certified on NT 4.0, recently completed Red Hat Linux Certified Engineer (RHCE) Training & cleared Brainbench.com "Linux General Administration" certification exam. Have been installing, configuring and developing on Linux patform for a long time now, have had programmed using C, C++, VC++, VB, COM, DCOM, MFC, ATL 3.0, PERL, Python, POSIX Threads and Linux Kernel programming; currently holding a total of 8 International Industry Certifications. For a list of all my articles at Linux.com (and other sites), click here.





  << Page 5 of 5