Up until now, it may have appeared that there is a one-to-one correspondence between a file descriptor and an open file. However, this is not the case. It is possible—and useful—to have multiple descriptors referring to the same open file. These file descriptors may be open in the same process or in different processes.
To understand what is going on, we need to examine three data structures maintained by the kernel:
the per-process file descriptor table;
the system-wide table of open file descriptions; and
the file system i-node table.
For each process, the kernel maintains a table of open file descriptors. Each entry in this table records information about a single file descriptor, including:
a set of flags controlling the operation of the file descriptor (there is just one such flag, the close-on-exec flag, which we describe in File Descriptors and exec()); and
The kernel maintains a system-wide table of all open file descriptions. (This table is sometimes referred to as the open file table, and its entries are sometimes called open file handles.) An open file description stores all information relating to an open file, including:
the current file offset (as updated by read() and write(), or explicitly modified using lseek());
status flags specified when opening the file (i.e., the flags argument to open());
the file access mode (read-only, write-only, or read-write, as specified in open());
settings relating to signal-driven I/O (Signal-Driven I/O); and
Each file system has a table of i-nodes for all files residing in the file system. The i-node structure, and file systems in general, are discussed in more detail in Chapter 14. For now, we note that the i-node for each file includes the following information:
Here, we are overlooking the distinction between on-disk and in-memory representations of an i-node. The on-disk i-node records the persistent attributes of a file, such as its type, permissions, and timestamps. When a file is accessed, an in-memory copy of the i-node is created, and this version of the i-node records a count of the open file descriptions referring to the i-node and the major and minor IDs of the device from which the i-node was copied. The in-memory i-node also records various ephemeral attributes that are associated with a file while it is open, such as file locks.
Figure 5-2 illustrates the relationship between file descriptors, open file descriptions, and i-nodes. In this diagram, two processes have a number of open file descriptors.
In process A, descriptors 1 and 20 both refer to the same open file description (labeled 23). This situation may arise as a result of a call to dup(), dup2(), or fcntl() (see Duplicating File Descriptors).
Descriptor 2 of process A and descriptor 2 of process B refer to a single open file description (73). This scenario could occur after a call to fork() (i.e., process A is the parent of process B, or vice versa), or if one process passed an open descriptor to another process using a UNIX domain socket (Passing File Descriptors).
Finally, we see that descriptor 0 of process A and descriptor 3 of process B refer to different open file descriptions, but that these descriptions refer to the same i-node table entry (1976)—in other words, to the same file. This occurs because each process independently called open() for the same file. A similar situation could occur if a single process opened the same file twice.
We can draw a number of implications from the preceding discussion:
Two different file descriptors that refer to the same open file description share a file offset value. Therefore, if the file offset is changed via one file descriptor (as a consequence of calls to read(), write(), or lseek()), this change is visible through the other file descriptor. This applies both when the two file descriptors belong to the same process and when they belong to different processes.
Similar scope rules apply when retrieving and changing the open file status flags (e.g., O_APPEND
, O_NONBLOCK
, and O_ASYNC
) using the fcntl() F_GETFL
and F_SETFL
operations.
By contrast, the file descriptor flags (i.e., the close-on-exec flag) are private to the process and file descriptor. Modifying these flags does not affect other file descriptors in the same process or a different process.