File descriptors are a core concept in Linux and other Unix-like operating systems. They provide a way for programs to interact with files, devices, and other input/output (I/O) resources.
Simply put, a file descriptor is like a “ticket” or “handle” that a program uses to access these resources. Every time a program opens a file or creates an I/O resource (like a socket or pipe), the operating system assigns it a unique number called a file descriptor.
This number allows the program to read, write, or perform other operations on the resource.
And as we all know, in Linux, almost everything is treated as a file—whether it’s a text file, a keyboard input, or even network communication. File descriptors make it possible to handle all these resources in a consistent and efficient way.
What Are File Descriptors?
A file descriptor is a non-negative integer assigned by your operating system whenever a program opens a file or another I/O resource. It acts as an identifier that the program uses to interact with the resource.
For example:
- When you open a text file, the operating system assigns it a file descriptor (e.g.,
3
). - If you open another file, it gets the next available file descriptor (e.g.,
4
).
These numbers are used internally by the program to perform operations like reading from or writing to the resource.
This simple mechanism allows programs to interact with different resources without needing to worry about how these resources are implemented underneath.
For instance, whether you’re reading from a keyboard or writing to a network socket, you use file descriptors in the same way!
The three standard file descriptors
Every process in Linux starts with three predefined file descriptors: Standard Input (stdin), Standard Output (stdout), and Standard Error (stderr).
Here’s a brief summary of their use:
Descriptor | Integer Value | Symbolic Constant | Purpose |
---|---|---|---|
stdin |
0 | STDIN_FILENO |
Standard input (keyboard input by default) |
stdout |
1 | STDOUT_FILENO |
Standard output (screen output by default) |
stderr |
2 | STDERR_FILENO |
Standard error (error messages by default) |
Now, let’s address each file descriptor with details.
1. Standard Input (stdin
)- Descriptor: 0
The purpose of the standard input stream is to receive input data. By default, it reads input from the keyboard unless redirected to another source like a file or pipe. Programs use stdin to accept user input interactively or process data from external sources.
When you type something into the terminal and press Enter, the data is sent to the program’s stdin
. This stream can also be redirected to read from files or other programs using shell redirection operators (<
).
One simple example of stdin
would be a script that takes input from the user and prints it:
#!/bin/bash
# Prompt the user to enter their name
echo -n "Enter your name: "
# Read the input from the user
read name
# Print a greeting message
echo "Hello, $name!"
Here’s what the output looks like:
But there is another way of using the input stream–redirecting the input itself. You can create a text file and redirect the input stream.
For example, here I have created a sample text file named input.txt
which contains my name Satoshi
. Later I redirected the input stream using <
:
As you can see, rather than waiting for my input, it took data from the text file and we somewhat automated this.
2. Standard Output (stdout
)- Descriptor: 1
The standard output stream is used for displaying normal output generated by programs. By default, it writes output to the terminal screen unless redirected elsewhere.
In simple terms, programs use stdout
to print results or messages. This stream can be redirected to write output to files or other programs using shell operators (>
or |
).
Let’s take a simple script that prints a greeting message:
#!/bin/bash
# Print a message to standard output
echo "This is standard output."
Here’s the simple output (nothing crazy but a decent example):
Now, if I want to redirect the output to a file, rather than showing it on the terminal screen, then I can use >
as shown here:
./stdout.sh > output.txt
Another good example can be the redirecting output of a command to a text file:
ls > output.txt
3. Standard Error (stderr
)- Descriptor: 2
The standard error stream is used for displaying error messages and diagnostics. It is separate from stdout
so that errors can be handled independently of normal program output.
For better understanding, I wrote a script that will trigger the stderr
signal as I have used the exit 1
to mimic a faulty execution:
#!/bin/bash
# Print a message to standard output
echo "This is standard output."
# Print an error message to standard error
echo "This is an error message." >&2
# Exit with a non-zero status to indicate an error
exit 1
But if you were to execute this script, it would simply print “This is an error message.” To understand better, you can redirect the output and error to different files.
For example, here, I have redirected the error message to stderr.log
and the normal output will go into stdout.log
:
./stderr.sh > stdout.log 2> stderr.log
Bonus: Types of limits on file descriptors
Linux kernel puts a limit on the number of file descriptors a process can use. These limits help manage system resources and prevent any single process from using too many. There are different types of limits, each serving a specific purpose.
- Soft Limits: The default maximum number of file descriptors a process can open. Users can temporarily increase this limit up to the hard limit for their session.
- Hard Limits: The absolute maximum number of file descriptors a process can open. Only the system admin can increase this limit to ensure system stability.
- Process-Level Limits: Each process has its own set of file descriptor limits, inherited from its parent process, to prevent any single process from overusing resources.
- System-Level Limits: The total number of file descriptors available across all processes on the system. This ensures fairness and prevents global resource exhaustion.
- User-Level Limits: Custom limits set for specific users or groups to allocate resources differently based on their needs.
Wrapping Up…
In this explainer, I went through what file descriptors are in Linux and shared some practical examples to explain their function. I tried to cover the types of limits in detail but then I had to drop the “detail” to stick to the main idea of this article.
But if you want, I can surely write a detailed article on the types of limits on file descriptors. Also, if you have any questions or suggestions, leave us a comment.