A number in a process

On Linux, a successful open returns a small nonnegative integer indexing the process's descriptor table. Operations such as read, write and lseek use that number to refer to the open file. The lowest currently unused descriptor is allocated, and a number can be reused after closure.

Its meaning is local to the process and its current state. Descriptor 3 in two processes need not designate the same object; two successive uses of 3 within one process may also concern different files. A pathname locates an object, while an already opened descriptor retains a reference. Removing or redirecting the pathname does not automatically redirect the existing descriptor.

References: [1]

Open file descriptions and sharing

An open file description holds state including a file offset and file status flags. A descriptor is a reference to that description, not the description itself. Separately opening the same pathname normally creates distinct open file descriptions, whereas dup allocates another descriptor referring to the existing one.

If descriptor 3 is duplicated as 4, changing the regular file's offset through 3 also changes the offset seen through 4. Duplication neither copies file contents nor creates an independent cursor. The descriptors share offset and file status flags, but not descriptor-specific flags such as close-on-exec. Keeping these layers distinct is necessary to understand why apparently separate handles can affect each other's I/O position.

References: [1] [2]

Standard streams and redirection

At startup, standard input, standard output and standard error normally correspond to descriptors 0, 1 and 2. They often refer to a terminal, but a parent process can arrange for files or other resources instead. The C-library symbols stdin, stdout and stderr represent FILE streams, not integer descriptor variables.

Dup2 makes a specified target descriptor refer to the same open file description as a source descriptor. For example, duplicating an opened output file onto descriptor 1 redirects subsequent writes through descriptor 1. Where replacement is needed, closing the old target and reusing its number occur atomically, avoiding the allocation race that would arise from treating them as two unrelated steps.

References: [2] [4]

Closing and number reuse

Close releases a descriptor so that its number can be reused. Other descriptors referring to the same open file description are not thereby closed. A saved integer is consequently not a durable identity that can safely be used after its ownership and lifetime have ended.

On Linux, successful closure does not guarantee that buffered data has reached persistent storage. Durability requires separate consideration of interfaces such as fsync and the storage system. Errors on closure should be checked, but blindly retrying the same number can close an unrelated resource if another thread has reused it. Portable programs must also consult the target system's interruption and error rules rather than assuming identical close semantics everywhere.

References: [3]