Linux Fundamentals · Processes & I/O
A Linux process can read a file, write a log, receive a network connection, and communicate with another program. It does not need to keep looking up a filename every time it performs one of those operations. Instead, it works through small numbers called file descriptors.
These numbers are a fundamental part of how Linux connects running programs to resources managed by the kernel. Understanding them explains why standard output is descriptor 1, how shell redirection works, why a deleted log file can still consume disk space, and how a web server communicates through a socket.
A file descriptor (FD) is a small, nonnegative integer that a process uses to refer to an open resource. The Linux kernel maintains a descriptor table for the process, allowing operations such as reading, writing, and closing that resource.
Descriptors can refer to regular files, pipes, terminals, sockets, and other kernel-supported objects. The familiar numbers 0, 1, and 2 conventionally represent standard input, standard output, and standard error.
- Why processes use file descriptors instead of repeatedly opening paths.
- How descriptor numbers relate to open resources.
- What happens when a program opens and closes a file.
- How descriptors connect files, pipes, and sockets.
- How to inspect descriptors and their state through
/proc. - How to open, read, and close a descriptor safely in Bash.
- The Mental Model: Process → Descriptor → Open Resource
- Linux File Descriptors Are Process-Local Numbers
- Why Are 0, 1, and 2 Special?
- What Happens When a Program Opens a File?
- File Descriptor vs. Open File Description
- Why File Descriptors Are More Than Files
- Pipes Use Descriptors
- Sockets Use Descriptors Too
- How Shell Redirection Changes File Descriptors
- Inspect Open File Descriptors Through /proc
- What Is /proc/PID/fdinfo?
- Mini Lab: Open a Descriptor and Watch It Change
- 1 Create a Temporary File
- 2 Open a File Descriptor
- 3 Inspect the Descriptor
- 4 Read Data and Observe the Offset
- 5 Close and Clean Up
- What Happens If a File Is Deleted While Still Open?
- Common Linux File Descriptor Mistakes
- When File Descriptors Help with Troubleshooting
- Knowledge Check
- Official References
The Mental Model: Process → Descriptor → Open Resource
Imagine a program reading a configuration file. The program initially knows the path to that file. It asks the operating system to open it. If the operation succeeds, the process receives a file descriptor it can use for subsequent I/O operations.
Conceptual model. The kernel maintains the references and state needed for I/O; the process uses the descriptor number.
The descriptor is not the file’s name, contents, or inode number. It is a reference used in the context of a particular process.
For a regular file, Linux also maintains an open file description. This kernel-level object includes information such as the current file offset and file status flags. The offset describes where the next sequential read or write normally occurs.
Think of a file descriptor as a numbered handle held by a process. The handle lets the program use an already-open resource without identifying it by pathname on every operation.
This model builds on how Linux processes work. A process is not just executable code: it also has runtime state, credentials, memory, and references to open resources.
Linux File Descriptors Are Process-Local Numbers
A file descriptor normally appears as an integer such as 0, 1, 2, or 3.
The number belongs to a process’s descriptor table. Descriptor 3 in one process does not necessarily refer to the same resource as descriptor 3 in another process.
For that reason, an FD number alone cannot identify a system-wide resource. You also need to know which process owns the descriptor.
Why Are 0, 1, and 2 Special?
Programs normally start with three standard I/O descriptors established by their launching environment.
| FD | Conventional name | Purpose |
|---|---|---|
0 |
stdin | Standard input: data supplied to the program. |
1 |
stdout | Standard output: normal program output. |
2 |
stderr | Standard error: diagnostics and error messages. |
3 and above |
Additional descriptors | Other open resources, with no universal assigned meaning. |
These are conventions rather than permanent connections to a keyboard or screen. Standard input might be connected to a file. Standard output might lead into a pipe. Standard error might be redirected to a log.
A program can also close one of these descriptors. Therefore, it is more accurate to say that processes normally have these standard connections than that every process must always keep all three open.
For practical examples of reconnecting these streams, see Linux Standard Streams Explained.
What Happens When a Program Opens a File?
Suppose a program needs to read /etc/hosts. It can ask the kernel to open that path for reading.
A simplified sequence looks like this:
- The program supplies a pathname and the intended access mode.
- The kernel resolves the pathname and evaluates the applicable access rules.
- If opening succeeds, the kernel creates or associates the required open-file state.
- The process receives a descriptor number.
- The program uses that descriptor for subsequent operations.
At the system-call level, the Linux open() interface returns a file descriptor on success. Later operations can use it with interfaces such as read(), write(), and close().
The pathname is important when the file is opened, but an already-open descriptor remains a reference to the opened object even if that pathname is later removed.
File Descriptor vs. Open File Description
These two terms look similar but represent different layers.
| Concept | What it represents | Important detail |
|---|---|---|
| Pathname | A name used to locate a filesystem object. | Names can change or disappear. |
| File descriptor | A process-local reference. | Its number is meaningful within the process. |
| Open file description | Kernel state associated with an open resource. | For regular files, includes the offset and file status flags. |
| Inode | A filesystem object and its metadata. | Several names may refer to the same inode. |
For example, opening the same regular file twice normally creates separate open file descriptions with independent offsets. Duplicating a descriptor with dup() is different: the original and duplicate refer to the same open file description and therefore share its file offset.
This distinction is one of the reasons an FD is not simply another name for a file.
Why File Descriptors Are More Than Files
The word file in file descriptor can be misleading. Linux uses descriptor-based interfaces for more than regular files stored on disk.
| Resource | What the descriptor enables | Example |
|---|---|---|
| Regular file | Reading or writing stored data. | Application configuration or logs. |
| Terminal | Interactive input and output. | A shell session. |
| Pipe | Passing a stream of bytes between endpoints. | One command feeding another. |
| Socket | Communicating through a socket endpoint. | A web server accepting client connections. |
| Kernel event object | Working with descriptor-based kernel interfaces. | An epoll instance or eventfd. |
Pipes Use Descriptors
Consider a shell pipeline:
BASH EXAMPLE
printf '%s\n' alpha beta | grep beta
The shell creates a pipe connecting the first command’s standard output to the second command’s standard input.
Behind the scenes, pipe endpoints are accessible through file descriptors. The first command writes bytes through its output descriptor. The second reads those bytes through its input descriptor.
The pipe does not need a regular filename in the filesystem.
Sockets Use Descriptors Too
A network server may create a socket, bind it to a network endpoint, and listen for connections. The socket API returns descriptors that the application can use to manage communication.
A TCP server can have one descriptor for its listening socket and additional descriptors for accepted client connections.
This is why a web server can handle many clients without inventing a separate file on disk for every network conversation.
The networking side is explained in Linux Ports and Sockets Explained. File descriptors provide the missing connection between the running application and those kernel-managed socket objects.
A descriptor is an access mechanism, not a promise that the resource is a disk file. The same process can hold descriptors for its logs, terminal, listening sockets, and client connections.
How Shell Redirection Changes File Descriptors
When you use redirection in Bash, the shell changes where selected file descriptors lead before the command executes.
For example:
ILLUSTRATIVE COMMAND — WRITES TO THE CURRENT DIRECTORY
printf '%s\n' hello > message.txt
The > operator redirects standard output to a file. Bash opens the output file and arranges for descriptor 1 to refer to the appropriate output destination.
The printf command does not need to know that output is going into a regular file rather than a terminal.
Similarly:
command > output.log 2> errors.log
This is illustrative shell syntax, not a command to run literally. It directs normal output through descriptor 1 to one file and diagnostic output through descriptor 2 to another.
Another commonly encountered form is:
command > combined.log 2>&1
Here, Bash first redirects descriptor 1 to the file. It then duplicates the current descriptor 1 destination onto descriptor 2.
Redirection order matters. Reversing those two operations can produce a different result because the shell processes redirections from left to right.
For more practice with shell commands and output handling, see Bash Scripting Basics.
Inspect Open File Descriptors Through /proc
Linux exposes process information through the /proc pseudo-filesystem. For a process with a known PID, the directory /proc/PID/fd provides entries representing its open descriptors, subject to permissions and system configuration.
On a system where Bash is available, you can inspect the descriptors of your current interactive shell.
READ-ONLY INSPECTION
echo $$
ls -l /proc/$$/fd
In Bash, $$ expands to the shell’s process ID. The second command asks Linux to list the descriptor entries associated with that process.
On a typical interactive system, you may see symbolic links for descriptors 0, 1, and 2. Their targets could be terminal devices, pipes, files, or other resources depending on how your shell was launched.
For example, a descriptor entry might point to a terminal device:
0 -> /dev/pts/0
1 -> /dev/pts/0
2 -> /dev/pts/0
This is illustrative output, not a universal result. Descriptor targets vary across SSH sessions, terminals, containers, and automation environments.
What Is /proc/PID/fdinfo?
The directory /proc/PID/fdinfo exposes additional information about individual descriptors.
For an ordinary open file, the information can include:
pos— the current file offset.flags— information about access mode and relevant flags.mnt_id— an identifier for the associated mount.ino— the inode number on Linux versions that expose this field.
These fields come from kernel-maintained state. Their availability and interpretation depend on the descriptor type and Linux version.
Inspection note: Access to another process’s descriptor information can be restricted. A permission error does not mean the process has no open files. Do not elevate privileges merely to complete this educational exercise.
Mini Lab: Open a Descriptor and Watch It Change
Goal: Create a temporary file, open it through a descriptor in Bash, inspect the descriptor, read two lines, and close it.
Requirements: A Linux environment with Bash, a mounted /proc filesystem, and standard utilities such as mktemp, cat, and ls.
Run the steps in the same interactive Bash session. No sudo is required. The only file created is a disposable temporary file.
1Create a Temporary File
sa_fd_file=$(mktemp)
printf 'alpha\nbeta\n' > "$sa_fd_file"
mktemp creates a temporary file and prints its pathname. The shell stores that path in sa_fd_file. The second command writes two lines into the file.
Expected result: The temporary file contains the lines alpha and beta. If mktemp reports an error, stop rather than continuing with an empty filename.
2Open a File Descriptor
exec {sa_reader}< "$sa_fd_file"
printf 'Descriptor: %s\n' "$sa_reader"
Bash supports allocating a descriptor and storing its number in a variable. The exec builtin with this redirection opens the file for reading in the current shell.
Expected result: Bash prints an allocated descriptor number, normally 10 or greater for this syntax.
The number is a handle inside the shell process. It is not the file’s inode or a permanent identifier.
3Inspect the Descriptor
ls -l "/proc/$$/fd/$sa_reader"
cat "/proc/$$/fdinfo/$sa_reader"
The first command shows the descriptor’s symbolic-link entry. The second displays information about the open descriptor.
For a regular file opened for reading, look for a pos field. Before reading data, its position should normally be zero.
Interpretation: You are observing a specific descriptor owned by your shell, rather than guessing which application has an unrelated file open.
4Read Data and Observe the Offset
IFS= read -r sa_line <&"$sa_reader"
printf 'Read: %s\n' "$sa_line"
cat "/proc/$$/fdinfo/$sa_reader"
The Bash read builtin reads the first line using the descriptor. The next command shows the descriptor information again.
Expected output:
Read: alpha
The pos value should now have advanced because the read operation consumed bytes from the file.
Read the second line:
IFS= read -r sa_line <&"$sa_reader"
printf 'Read: %s\n' "$sa_line"
Expected output:
Read: beta
You did not supply the pathname to read. You supplied an existing descriptor that already references the open file.
5Close and Clean Up
exec {sa_reader}<&-
rm -- "$sa_fd_file"
unset sa_fd_file sa_reader sa_line
The first command closes the descriptor. The second removes only the temporary file created for this exercise. The final command clears the practice variables.
Result: The descriptor is no longer open in the shell, and the temporary file has been removed.
Opening the pathname produced a descriptor. The descriptor let Bash read the file without looking up the pathname again. The open file description maintained an offset as reading progressed. Closing the descriptor released that reference.
What Happens If a File Is Deleted While Still Open?
Removing a pathname and closing an open descriptor are different operations.
On a conventional Linux filesystem, removing a file’s final directory entry does not necessarily release the underlying file data immediately. A process may still have the file open.
Until the relevant open references are released, the filesystem may need to retain the underlying object and its storage.
This explains a real troubleshooting situation: an administrator removes a large application log, but the expected disk space does not immediately become available. One possible reason is that the application still has the deleted file open.
It is not the only possible explanation for missing free space, and removing more files without diagnosis is not a reliable solution.
For the related distinction between a filename and its inode, review Hard Links vs Symbolic Links in Linux.
Production warning: Do not delete active application logs as a substitute for proper log rotation. Investigate the owning process, logging configuration, and service behavior before changing production files.
Common Linux File Descriptor Mistakes
A pathname identifies a filesystem location. A descriptor is a process-local reference to an already-open resource. The two are related but not interchangeable.
Two processes can use the same descriptor number for completely different resources. Always identify the process as well as the descriptor.
Pipes, sockets, terminals, and other kernel interfaces can all be accessed through descriptors. Not every descriptor corresponds to a regular file.
Duplicated descriptors can reference the same open file description and share its offset. Separate successful opens normally establish distinct open file descriptions.
Closing a descriptor releases the process’s reference. It does not, by itself, guarantee that previously written data has reached durable storage. Applications requiring durability need appropriate synchronization and error-handling behavior.
When a process launches another program, open descriptors can sometimes be inherited. Close-on-exec controls help prevent unintended inheritance. This matters when designing services that handle private files or connections.
When File Descriptors Help with Troubleshooting
File descriptors become particularly useful when a Linux application reports symptoms involving resources that appear to exist but cannot be accessed.
Consider an application reporting Too many open files. One possible cause is that the process has reached a relevant limit on open file descriptors.
That message does not tell you which resources are open or why they remain open. Begin by establishing evidence rather than immediately increasing limits.
- Identify the process. Find the correct PID and confirm that it belongs to the affected workload.
- Inspect descriptor entries. Where permissions allow, examine
/proc/PID/fd. - Understand the resource types. Determine whether the process holds regular files, sockets, pipes, or other objects.
- Check the relevant limits. Compare the application’s descriptor use with the limits actually applied to that process.
- Investigate the cause. Determine whether descriptor usage is expected for the workload or could indicate resources not being closed.
- Choose the appropriate correction. Fix unintended resource retention or adjust a justified limit using the service’s supported configuration mechanism.
The optional lsof utility can provide an additional view of open resources, while Linux /proc exposes kernel-maintained descriptor information directly.
Both tools have permission and visibility limitations. A snapshot also cannot prove that an application has a descriptor leak: resource counts need to be interpreted over time and against the workload.
The central principle remains the same: understand what the process has open before deciding that a limit, filesystem, or network setting must change.
Knowledge Check
Try answering each question before expanding the explanation.
1. Why does Linux give a process a descriptor when it opens a file?
The descriptor gives the process a compact reference to the open resource. Subsequent I/O operations can use that reference rather than repeatedly resolving a pathname.
2. Can descriptor 3 refer to different things in different processes?
Yes. Descriptor numbers are process-local. FD 3 might reference a file in one process and a socket in another.
3. Why can a pipe use file descriptors even though it has no ordinary filename?
Descriptors are references to open resources, not exclusively to named disk files. A pipe provides endpoints through which processes can read and write bytes.
4. What is the difference between opening a file twice and duplicating an existing descriptor?
Separate opens normally create independent open file descriptions, including independent offsets. A duplicate descriptor refers to the same open file description as the original, so they share the file offset and file status flags.
5. Why might deleting a large file fail to release its disk space immediately?
A process may still hold an open reference to the file. Removing its final pathname does not necessarily release the underlying storage until the remaining references are gone.
6. What would you inspect first if a service reports “Too many open files”?
Identify the affected process, inspect its open descriptor entries and their resource types, then compare its usage with the relevant limits. Investigate whether the resources are expected before changing configuration.
A Linux file descriptor is not a file, a filename, or a globally unique resource identifier. It is a small number that a process uses to refer to an open resource through kernel-maintained state.
That simple model connects several areas of Linux administration: standard streams, shell redirection, filesystems, pipes, sockets, and process troubleshooting.
When a program interacts with data, ask two questions: Which process is doing the work, and what resource does its descriptor reference? Those questions reveal how programs communicate with the operating system and give you a stronger foundation for understanding server behavior.
Official References
- Linux man-pages — open(2): descriptor allocation and open file descriptions.
- Linux man-pages — proc_pid_fd(5): descriptor entries under /proc.
- Linux man-pages — proc_pid_fdinfo(5): descriptor metadata and offsets.
- Linux man-pages — dup(2): duplicate descriptors and shared open-file state.
- Linux man-pages — close(2): closing descriptors and resource lifetime.
- GNU Bash Reference Manual — Redirections: shell descriptor manipulation and redirection rules.








