What is a file descriptor?
A file descriptor (FD) is a small non-negative integer that the operating system kernel uses to represent an open I/O resource within a process. When a process opens a file, creates a socket, or establishes a pipe, the kernel returns an FD — typically the smallest available integer starting from 0. The process then uses this integer in subsequent system calls (`read`, `write`, `close`, `select`, `epoll_ctl`) to refer to that resource without needing to know its underlying type.
Every process starts with three FDs open by convention: 0 (stdin), 1 (stdout), and 2 (stderr). These are just entries in the process's file descriptor table like any other.
Under the hood, the kernel maintains a per-process **file descriptor table** — an array where each index (the FD) points to an entry in a system-wide **open file table**. The open file table entry stores the file offset (current read/write position), access mode (read/write/append), and a pointer to the underlying **vnode** (inode reference). Multiple FDs, potentially from different processes, can point to the same open file table entry — this happens after `fork()`, where parent and child share file offsets.
The UNIX design philosophy of "everything is a file" means FDs abstract over: regular files, directories, character and block devices, named and anonymous pipes (FIFOs), sockets (TCP, UDP, Unix domain), terminal devices (TTYs), and special files like `/dev/null` and `/dev/random`. Calling `read()` on a socket, a pipe, or a file uses the same syscall — the kernel dispatches to the correct implementation via the vnode's operation table.
FDs are process-local. The same integer in two different processes refers to different resources. FDs are inherited across `fork()` (unless marked `O_CLOEXEC`) and can be passed between processes via Unix domain sockets using `sendmsg` with `SCM_RIGHTS`.
Operating systems impose limits on open FDs: `ulimit -n` shows the per-process soft limit (often 1024 or 65536). Servers handling many concurrent connections must raise this limit. Leaking FDs — opening resources without closing them — exhausts the table and causes `EMFILE` errors, a common production bug.
Explains that an FD is an integer index into a per-process table, names stdin/stdout/stderr as 0/1/2, and gives two concrete examples of resource types (file, socket, pipe).
Describes the three-level kernel structure (FD table → open file table → vnode), explains FD sharing after fork(), mentions O_CLOEXEC, addresses ulimit and FD leak scenarios.
Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.
Get Graded — Free Assessment