In Unix and Linux systems, almost everything—regular files, directories, pipes, network sockets, and character devices—is represented as a stream of bytes accessed through a file descriptor (FD).

Standard File Descriptors

By default, every new Unix process starts with three open file descriptors:

FD Number Name Description Default Target
0 stdin Standard Input Keyboard / Stream input
1 stdout Standard Output Terminal display
2 stderr Standard Error Terminal display (unbuffered error stream)

Under the Hood: Per-Process vs Kernel File Table

When your process calls open() or socket(), the kernel doesn’t give user space direct access to internal memory structures. Instead:

  1. It allocates a non-negative integer in your process’s File Descriptor Table (e.g., 3, 4, etc.).
  2. That entry points to the system-wide Open File Description Table in the kernel, tracking the file offset, status flags (like non-blocking), and access mode.
  3. That entry points to the file’s inode table entry on the disk.
[Process FD Table]  ──>  [Kernel Open File Table]  ──>  [Inode / Device]
FD 3 (stdout/pipe)        Offset: 1024, Mode: Read       File on disk

Checking Open Descriptors for a Process

You can inspect the file descriptors of any running process in Linux via /proc:

ls -l /proc/<PID>/fd

This simple, unified abstraction makes redirection (>, 2>&1, |) in Unix shells extraordinarily clean, predictable, and composable.