Learning Question
When a program opens a file, what does the operating system actually give it?
A file path is a name used to find something.
An open file is an operating-system-managed resource created after the path has been resolved and access has been checked.
A file descriptor is a small process-local integer that refers to that open resource.
The first mental model is:
A file descriptor is not the file itself. It is a process-local handle to an open OS-managed resource.
Path Versus Open Resource
A path such as notes.txt is a way to name a file in a file system.
Opening the path is a runtime operation.
During an open operation, the kernel may:
- resolve the path relative to a directory
- check permissions
- create or truncate a file if requested
- allocate an open file description
- attach flags and current offset state
- return a file descriptor to the process
After the file is open, later reads and writes usually use the descriptor, not the path.
This distinction matters because the path and the open resource can diverge.
A file may be renamed or removed from a directory while a process still holds an open descriptor to it.
File Descriptors Are Process-Local
On Unix/Linux, file descriptors are small integers such as 0, 1, and 2.
By convention:
0is standard input1is standard output2is standard error
These numbers are meaningful inside a process because the process has a descriptor table.
Another process may also have descriptor 3, but that number does not automatically refer to the same open resource.
The descriptor number is process-local.
The kernel object behind it is OS-managed.
Open Resources Are Broader Than Disk Files
The word “file descriptor” can mislead if it is understood too narrowly.
On Unix/Linux, descriptors can refer to many open resources:
- regular files
- directories
- terminals
- pipes
- sockets
- devices
- event or timer objects on some systems
This is why the same read and write style interface can apply to different kinds of resources.
The descriptor is a uniform reference.
The underlying resource determines what reading or writing means.
Descriptor Tables and Inheritance
A process has a table that maps descriptor numbers to open resources.
When a process creates a child process, the child may inherit descriptors from the parent.
This inheritance is central to Unix/Linux shell behavior.
For example, a shell can set up standard output before starting a program.
The program then writes to descriptor 1 without needing to know whether output goes to a terminal, a file, or a pipe.
The descriptor table creates indirection:
Program code writes to a descriptor. The OS decides which open resource that descriptor currently refers to.
Offsets, Flags, and Shared Open State
An open file can have associated state.
For regular files, an important example is the current file offset.
A read may advance that offset.
A write may also advance it.
Some state belongs to the descriptor entry, and some belongs to the underlying open file description. The exact distinction matters in advanced cases such as dup, fork, and shared offsets.
For the first model, the useful point is:
Opening a file creates runtime state. File access is not just repeated lookup of a path string.
Closing Descriptors
When a process no longer needs an open resource, it should close the descriptor.
Closing removes the process’s reference.
If no references remain, the operating system can release the underlying resource state.
If a process exits, the OS closes its remaining descriptors as part of cleanup.
This is one reason leaking descriptors matters.
A program may accidentally keep files, sockets, or pipes open longer than intended, which can change behavior for other processes.
Core Mental Model
Paths name file-system entries.
File descriptors refer to open runtime resources.
When reading program behavior, ask:
Is this code working with a path name, or is it working with an already-open descriptor that the OS tracks for this process?
Final Summary
File descriptors are process-local handles to OS-managed open resources.
They let programs interact with files, terminals, pipes, sockets, and devices through a small integer while the operating system manages the actual resource, permissions, offset state, inheritance, and cleanup.