A file descriptor is a reference to the basic file handle granted by UN*X-style operating systems. File descriptors are stored in ints, probably for historical reasons; only nonnegative ints are valid descriptors. The OS gives each process a separate namespace for its file descriptors.
Files with available descriptors are typically called open. However the terminology is imprecise, as many file handles may be attached to the file, each file handle can have many file descriptors, and each of these can be closed separately. Still, it is usually clear what is meant.
UN*X philosophy is that "everything is a file". Well, not really. Everything is accessed by a file handle. This includes files (of course), objects that appear to live in the file system such as devices, all types of sockets or pipes, and more! The OS never lets a program see a file handle -- it always uses a descriptor to that file handle. Creating a new file handle is accomplished in many ways; all return a file descriptor to that handle:
- Objects in the file system use open;
- Network sockets use socket (to create an unconnected socket) or accept (to respond to a connection attempt);
- Unnamed pipes use pipe or socketpair.
The
dup and
dup2 system calls
duplicate a file descriptor: they return a new descriptor attached to the same handle. No trickery is required to pass file descriptors to a child process:
fork (also
vfork) have no effect on file descriptors, and they remain valid in
both parent and
child after the call. It is also possible to transfer file descriptors between unrelated processes, but in practice different UN*Xes may require subtly different code to do this.
Once you have a file descriptor, you can pass it to many system calls. The standard ones are close, read, and write, and work on any file descriptor. Once you close the file descriptor, you may no longer pass it to system calls; however, the file handle itself may continue to exist. select, poll and fcntl also work on any file descriptor, but are really only useful on file descriptors which are not attached to files.
More specialized system calls include ioctl (mostly for devices) and the socket-only calls (accept, mentioned above, is one of these, as are bind, listen, connect, and setsockopt).
File descriptors are a complete misnomer: They are opaque handles not descriptors, and they do not necessarily describe files. Nonetheless, they are easy to use and offer a small consistent toolbox for handling a great number of very different objects.
A surprisingly long entry in the BQ2K6