19.2 FIFOs

Although pipes are a simple, flexible, and efficient communication mechanism, they have one main drawback—namely, that there is no way to open an already existing pipe. This makes it impossible for two arbitrary processes to share the same pipe, unless the pipe was created by a common ancestor process.

This drawback is substantial for many application programs. Consider, for instance, a database engine server, which continuously polls client processes wishing to issue some queries and which sends the results of the database lookups back to them. Each interaction between the server and a given client might be handled by a pipe. However, client processes are usually created on demand by a command shell when a user explicitly queries the database; server and client processes thus cannot easily share a pipe.

To address such limitations, Unix systems introduce a special file type called a named pipe or FIFO (which stands for "first in, first out"; the first byte written into the special file is also the first byte that is read). Any FIFO is much like a pipe: rather than owning disk blocks in the filesystems, an opened FIFO is associated with a kernel buffer that temporarily stores the data exchanged by two or more processes.

Thanks to the disk inode, however, a FIFO can be accessed by any process, since the FIFO filename is included in the system's directory tree. Thus, in our example, the communication between server and clients may be easily established by using FIFOs instead of pipes. The server creates, at startup, a FIFO used by client programs to make their requests. Each client program creates, before establishing the connection, another FIFO to which the server program can write the answer to the query and includes the FIFO's name in the initial request to the server.

In Linux 2.4, FIFOs and pipes are almost identical and use the same pipe_inode_info structures. As a matter of fact, the read and write file operation methods of a FIFO are implemented by the same pipe_read( ) and pipe_write( ) functions described in the earlier sections Section 19.1.4 and Section 19.1.5. Actually, there are only two significant differences:

·         FIFO inodes appear on the system directory tree rather than on the pipefs special filesystem.

·         FIFOs are a bidirectional communication channel; that is, it is possible to open a FIFO in read/write mode.

To complete our description, therefore, we just have to explain how FIFOs are created and opened.

19.2.1 Creating and Opening a FIFO

A process creates a FIFO by issuing a mknod( )[4] system call (see Section 13.2), passing to it as parameters the pathname of the new FIFO and the value S_IFIFO (0x1000) logically ORed with the permission bit mask of the new file. POSIX introduces a function named mkfifo( ) specifically to create a FIFO. This call is implemented in Linux, as in System V Release 4, as a C library function that invokes mknod( ).

[4] In fact, mknod( ) can be used to create nearly any kind of file, such as block and character device files, FIFOs, and even regular files (it cannot create directories or sockets, though).

Once created, a FIFO can be accessed through the usual open( ), read( ), write( ), and close( ) system calls, but the VFS handles it in a special way because the FIFO inode and file operations are customized and do not depend on the filesystems in which the FIFO is stored.

The POSIX standard specifies the behavior of the open( ) system call on FIFOs; the behavior depends essentially on the requested access type, the kind of I/O operation (blocking or nonblocking), and the presence of other processes accessing the FIFO.

A process may open a FIFO for reading, for writing, or for reading and writing. The file operations associated with the corresponding file object are set to special methods for these three cases.

When a process opens a FIFO, the VFS performs the same operations as it does for device files (see Section 13.2.3). 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 special file, and invokes if necessary the init_special_inode( ) function. It turn, this function sets the i_fop field of the inode object to the address of the def_fifo_fops table. Later, the kernel sets the file operation table of the file object to def_fifo_fops, and executes its open method, which is implemented by fifo_open( ).

The fifo_open( ) function initializes the data structures specific to the FIFO; in particular, it performs the following operations:

1.       Acquires the i_sem inode semaphore.

2.       Checks the i_pipe field of the inode object; if it is NULL, it allocates and initializes a new pipe_inode_info structure, as in Step 1 in the earlier section Section 19.1.3.

3.       Depending on the access mode specified as the parameter of the open( ) system call, it initializes the f_op field of the file object with the address of the proper file operation table (see Table 19-4).

Table 19-4. FIFO's file operations

Access type

File operations

read method

write method

Read-only

read_fifo_fops

pipe_read( )

bad_pipe_w( )

Write-only

write_fifo_fops

bad_pipe_r( )

pipe_write( )

Read/write

rdwr_fifo_fops

pipe_read( )

pipe_write( )

4.       If the access mode is either read-only or read/write, it adds one to the readers and r_counter fields of the pipe_inode_info structure. Moreover, if the access mode is read-only and there is no other reading process, it wakes up any writing process sleeping in the wait queue.

5.       If the access mode is either write-only or read/write, it adds one to the writers and w_counter fields of the pipe_inode_info structure. Moreover, if the access mode is write-only and there is no other writing process, it wakes up any reading process sleeping in the wait queue.

6.       If there are no readers or no writers, it decides whether the function should block or terminate returning an error code (see Table 19-5).

Table 19-5. Behavior of the fifo_open( ) function

Access type

Blocking

Nonblocking

Read-only, with writers

Successfully return

Successfully return

Read-only, no writer

Wait for a writer

Successfully return

Write-only, with readers

Successfully return

Successfully return

Write-only, no reader

Wait for a reader

Return -ENXIO

Read/write

Successfully return

Successfully return

7.       Releases the inode semaphore, and terminates, returning 0 (success).

The FIFO's three specialized file operation tables differ mainly in the implementation of the read and write methods. If the access type allows read operations, the read method is implemented by the pipe_read( ) function. Otherwise, it is implemented by bad_pipe_r( ), which just returns an error code. Similarly, if the access type allows write operations, the write method is implemented by the pipe_write( ) function; otherwise, it is implemented by bad_pipe_w( ), which also returns an error code.