Linux File Descriptors Explained: Open Files, Sockets, and Limits

Linux file descriptors illustrated as connections between a process, file storage, a pipe, and a network socket Server Basics
Every Linux process uses file descriptors to work with files, terminal streams, pipes, and network sockets. This practical lesson explains how descriptors work, how to inspect them through /proc and lsof, and what open-file limits mean for server troubleshooting.
Linux & Administration · Process Fundamentals

Linux file descriptors are the small numeric handles that processes use to access open files, pipes, sockets, and other kernel-managed resources. Every time a program reads a configuration file, writes a log, or communicates through a network socket, file descriptors can be part of the operation.

For a Linux administrator, understanding these handles makes several familiar problems easier to explain: why a process keeps a deleted log file open, how two commands communicate through a pipe, and what an application means when it reports Too many open files.

This lesson explains the underlying model, shows how to inspect it using standard Linux interfaces, and introduces a safe exercise that opens and closes a descriptor without changing system configuration.

Quick Answer

A file descriptor is a nonnegative integer that a process uses to refer to an open resource. Linux maintains a descriptor table for the process, allowing a program to read, write, or perform other operations through those references.

Descriptors 0, 1, and 2 conventionally represent standard input, standard output, and standard error. Other descriptors can represent regular files, pipes, sockets, and additional resources.

Use /proc/PID/fd/ to inspect a process’s descriptor entries and /proc/PID/limits to inspect its resource limits. The optional lsof utility provides another way to investigate open resources.

What You’ll Learn

  • What a file descriptor represents inside a Linux process.
  • How descriptors relate to files, pipes, and network sockets.
  • Why file descriptor numbers are local to a process.
  • How to inspect descriptors through /proc and lsof.
  • What soft and hard open-file limits mean.
  • How to investigate a possible descriptor leak without changing production settings.

What Are Linux File Descriptors?

A program needs a way to refer to resources it has opened. Rather than repeatedly identifying a file by its pathname, a program can ask the kernel to open it and then work through the descriptor returned by that operation.

The Linux open() system call, for example, returns a nonnegative integer when it successfully opens a file. Subsequent operations can use that integer to identify the open resource.

Running Process
Descriptor Table
Open Resource

Simplified conceptual model. The kernel also maintains open-file descriptions and other objects behind these references.

Think of a descriptor as a handle rather than the resource itself. The number tells the process which open connection to use. It is not the file’s name, inode number, or permanent identity.

This builds on the distinction introduced in Linux Processes Explained. A process is a running program with its own execution state and resources. File descriptors are one part of that state.

File Descriptor Numbers Are Process-Local

Suppose two separate processes both have descriptor 5 open. The numbers match, but they do not necessarily refer to the same resource.

Each process has its own descriptor context. Descriptor 5 in one process may refer to a log file, while descriptor 5 in another may refer to a network socket.

Processes can also intentionally share open resources through mechanisms such as inheritance or descriptor passing. Therefore, equal descriptor numbers do not prove sharing, and different descriptor numbers do not necessarily prove that the underlying open resource is different.

Academy Insight

A descriptor number is meaningful only in the context of the process that owns it. To understand what a descriptor refers to, identify both the process and the descriptor.

Why Descriptors 0, 1, and 2 Are Special

Linux programs commonly begin with three standard streams already connected by the environment that launches them.

Descriptor Standard Stream Common Purpose
0 stdin Input to the program
1 stdout Normal program output
2 stderr Diagnostics and errors

These numbers describe conventional roles, not fixed physical destinations. In an interactive terminal, standard input may be connected to your terminal, while standard output and standard error normally appear in the same terminal window.

Redirection changes those connections.

printf '%s\n' "Server ready" > status.txt

In this example, Bash opens status.txt for output and arranges for the command’s standard output to write there.

The command does not need to know whether descriptor 1 points to a terminal or a file.

For a fuller explanation of redirection order, pipes, and standard streams, read Linux Standard Streams Explained.

File Descriptors Are Not the Same as Open Files

The distinction between a descriptor and an open file description is important when investigating Linux behavior.

A descriptor is the process-facing reference. An open file description is a kernel-maintained object containing information associated with the open instance, including file status flags and, where applicable, a current file offset.

Several descriptors may refer to the same open file description. For example, duplicating a descriptor with the dup() family of system calls can create another descriptor that shares the underlying open-file state.

By contrast, opening the same pathname independently can produce separate open file descriptions, each with its own file offset.

This helps explain why reading through one duplicated descriptor can affect where a subsequent read through another duplicated descriptor begins.

A Filename Can Disappear While the File Remains Open

Linux separates pathnames from open references to filesystem objects.

Consider a running application that has opened a log file. If its pathname is removed, the application may still hold an open descriptor referencing the underlying file.

As a result, removing the pathname does not necessarily release the file’s storage immediately. Reclamation can be delayed while open references remain.

The related lesson on hard links, symbolic links, and inodes explains why a pathname and a filesystem object are different concepts.

For administrators, this distinction matters when disk usage remains unexpectedly high after a large log file has been deleted.

How to Inspect Linux File Descriptors

On Linux systems with the usual procfs interface available, the directory /proc/PID/fd/ exposes descriptor entries for a process.

Each entry is named after a descriptor number and commonly appears as a symbolic link describing the referenced resource.

Inspect Your Current Shell

In Bash, $$ identifies the current shell process for this basic inspection.

printf 'Shell PID: %s\n' "$$"

ls -l "/proc/$$/fd"

The output will depend on your environment.

You may see descriptor entries pointing to terminal devices, pipes, or files. This is an observation of the current process state, not a permanent inventory.

The open descriptors of a running process can change while you inspect them.

Inspect Another Process

If you know the PID of a process and have permission to inspect it, replace 1234 below with the real PID.

ls -l /proc/1234/fd

You may encounter several kinds of entries.

Example Target What It Suggests
/var/log/app.log An open filesystem object associated with that path.
pipe:[...] A pipe endpoint.
socket:[...] A socket object.
anon_inode:[...] A kernel object represented through an anonymous inode interface.

A socket entry does not automatically reveal the remote address or application protocol. You may need additional networking information to understand the connection.

Linux also exposes descriptor-related details through /proc/PID/fdinfo/, including information such as the file position and flags for supported descriptor types.

These interfaces are part of the broader virtual filesystem model described in Linux File System Explained.

Access to another process’s descriptor information can be restricted by permissions, security policies, process namespaces, or the way /proc is mounted.

Inspect Open Resources with lsof

The lsof utility provides a convenient human-readable view of open resources.

If it is installed, use:

lsof -p 1234

Replace 1234 with the PID you want to investigate.

The output can identify regular files, sockets, libraries, and other process-related resources.

Remember that not every row corresponds to one ordinary numeric file descriptor. Entries describing the current working directory, executable text, or memory mappings can also appear.

Therefore, counting every lsof row is not a reliable way to determine how many numeric descriptors a process currently holds.

The utility is optional and may not be installed on a minimal server image.

Why Network Sockets Also Use File Descriptors

A file descriptor does not have to refer to a regular file stored on disk.

Linux also uses descriptors for sockets, allowing applications to interact with network connections through kernel-managed handles.

A web server, for example, may hold a listening socket and additional sockets associated with accepted connections.

As the server handles more connections, its descriptor usage may grow. The exact relationship depends on application architecture and connection management.

This connects directly to Linux Ports and Sockets Explained, which covers listening addresses, connection states, and the ss command.

Important Distinction

A TCP port number identifies a transport-layer endpoint. A file descriptor is a process-local handle used to access a socket object. They are related, but they are not the same identifier.

Linux File Descriptors and Open-File Limits

Processes cannot necessarily open an unlimited number of file descriptors.

Linux provides a per-process resource limit called RLIMIT_NOFILE. It constrains the descriptor numbers that a process can allocate.

Administrators commonly encounter this limit through the shell’s ulimit builtin or the process information exposed under /proc.

Soft Limit vs. Hard Limit

A resource limit has two values.

The soft limit is the value enforced during normal operation.

The hard limit is the ceiling to which an unprivileged process can generally raise its soft limit, subject to the applicable rules.

In Bash, inspect the current shell’s open-file limits with:

ulimit -Sn
ulimit -Hn

These commands display the soft and hard limits, respectively. They do not change the values.

The numbers vary by distribution, service configuration, and execution environment.

Inspect a Running Process’s Limits

For a running process, examine:

cat /proc/1234/limits

Replace 1234 with the real PID.

Look for the Max open files entry. The output reports the process’s soft and hard limits.

This is more useful than assuming a running service has the same limits as your interactive shell.

A service manager, container runtime, or other launching environment may establish limits independently of the terminal session you are using.

Changing ulimit in your shell does not retroactively change the limits of an unrelated service that is already running.

What Does “Too Many Open Files” Mean?

An application may report Too many open files when it cannot allocate another descriptor because it has reached its per-process limit.

The corresponding Linux error is commonly EMFILE.

A separate error, ENFILE, indicates exhaustion of a system-wide open-file resource limit.

These conditions are different from running out of free disk space.

Imagine a long-running service that opens a connection for each incoming request but fails to release certain connections after use.

Over time, its descriptor usage could increase until opening another socket or file fails.

This is a hypothetical example, not a measured incident. The point is the troubleshooting sequence: increasing a limit may postpone a failure without fixing a resource leak.

A Safer Diagnostic Sequence

  1. Identify the affected process and verify the reported error.
  2. Inspect the process’s current descriptor entries.
  3. Check its soft and hard open-file limits.
  4. Look for unexpectedly large or growing groups of files or sockets.
  5. Review application behavior, connection lifecycle, and relevant logs.
  6. Consider changing limits only after understanding the expected workload and the cause of the resource use.

Do not immediately raise system-wide limits or restart critical services without first establishing what is happening.

A restart may temporarily release open resources, but it can also destroy evidence of how the problem developed.

Mini Lab: Open and Close a File Descriptor

Lab Objective

Create a temporary text file, open it through an additional Bash descriptor, inspect the connection, read its contents, and close the descriptor.

This exercise requires Bash and a Linux system with /proc available. It does not require administrator privileges.

Run the commands in one disposable lab shell, not in a critical automation session.

Step 1: Create a Temporary Workspace

SA_FD_LAB=$(mktemp -d)

printf '%s\n' "descriptor demo" \
  > "$SA_FD_LAB/note.txt"

printf 'Workspace: %s\n' "$SA_FD_LAB"

The file is created only inside the newly generated temporary directory.

Step 2: Open Descriptor 3

exec 3< "$SA_FD_LAB/note.txt"

This Bash redirection opens the file for reading on descriptor 3 in the current shell.

The command does not replace your shell with another program. The exec builtin can also apply redirections to the current shell.

Step 3: Inspect the Open Descriptor

ls -l "/proc/$$/fd/3"

readlink "/proc/$$/fd/3"

The descriptor entry should point to the temporary text file.

This demonstrates the relationship between a numeric handle and an open resource.

Step 4: Read Through Descriptor 3

IFS= read -r sa_line <&3

printf 'Read: %s\n' "$sa_line"

You should see:

Read: descriptor demo

Bash reads the file through descriptor 3 rather than opening the pathname again for this read.

Step 5: Close the Descriptor

exec 3<&-

The descriptor is now closed in the shell.

To confirm the descriptor entry is gone:

if test -L "/proc/$$/fd/3"; then
  printf 'Descriptor 3 is still open\n'
else
  printf 'Descriptor 3 is closed\n'
fi

Expected result:

Descriptor 3 is closed

Step 6: Clean Up

First confirm that the saved path is the temporary workspace created in Step 1.

printf 'Cleaning: %s\n' "$SA_FD_LAB"

Then remove only the practice file and its empty directory:

rm -- "$SA_FD_LAB/note.txt"
rmdir -- "$SA_FD_LAB"
unset SA_FD_LAB sa_line

The exercise demonstrates the descriptor lifecycle: open → inspect → read → close.

Common Linux File Descriptor Mistakes

1. Assuming Every Descriptor Is a Disk File

Descriptors can represent sockets, pipes, and other resources. A high descriptor count does not automatically mean an application has opened thousands of ordinary files.

2. Treating a Descriptor Number as a Global Identifier

Descriptor 4 in one process is not automatically related to descriptor 4 in another.

3. Confusing Open-File Limits with Disk Capacity

A process can exhaust available descriptors while substantial disk space remains free.

4. Assuming a Deleted File Has Released Its Storage

Open references may keep the underlying file object alive after its pathname disappears.

5. Changing Limits Before Investigating Resource Usage

Raising a descriptor limit may be appropriate for a legitimate workload, but it should not be the first response to unexplained growth in open resources.

6. Assuming Your Shell’s Limits Apply to Every Service

Always inspect the running process. Services can inherit resource limits from different execution environments.

When Should You Use Each Inspection Tool?

Question Useful Tool
Which descriptors does a process currently hold? /proc/PID/fd/
What additional descriptor information is exposed? /proc/PID/fdinfo/
Which files and resources are associated with a process? lsof -p PID
What open-file limits apply to a process? /proc/PID/limits
What limits apply to this Bash shell? ulimit -Sn and ulimit -Hn
Which network sockets are listening or connected? ss

Each tool answers a different question. Start with the process and symptom, then choose the inspection method that reveals the relevant layer.

Knowledge Check

Try answering these questions before opening the explanations.

1. Why can two processes both use descriptor 5 without referring to the same file?

Descriptor numbers are interpreted within a process’s descriptor table. Equal numeric values in different processes do not establish that their resources are related.

2. Why is a file descriptor different from a filename?

A filename is a pathname used to locate a filesystem object. A descriptor is an existing process-level reference to an open resource. The reference can remain valid even if the original pathname changes or disappears.

3. Can a network connection consume a file descriptor?

Yes. Linux applications access socket objects through file descriptors. A descriptor therefore does not have to represent a regular file.

4. Why might changing ulimit in your terminal not fix a running server?

The existing server process may have inherited limits from another environment. Changing the limits of your interactive shell does not retroactively modify an unrelated running process.

5. What should you investigate before increasing an open-file limit?

Check the affected process, its current limit, the kinds of resources it holds, whether usage is growing unexpectedly, and whether the application is releasing resources correctly.

6. Does an lsof output row always represent one numbered file descriptor?

No. The utility can also show resources such as working directories, executable references, and memory mappings. Not every row is one numeric descriptor.

Servers Academy — Key Takeaway

A Linux file descriptor is a process-local handle to an open resource. The process uses a small integer to refer to something the kernel manages, whether that resource is a file, pipe, socket, or another supported object.

The most useful mental model is to separate three things: the process, its descriptor table, and the underlying resource. They are connected, but they are not interchangeable.

For administration, start with observation. Inspect /proc/PID/fd/, examine the process’s actual limits, and identify the resources involved before changing configurations or restarting workloads.

Official References

The following primary technical references document the underlying Linux interfaces and commands.

Rate article
Servers Academy
Add a comment