Too many open files: whose limit it is, why it is not shared, and what counts as a descriptor
"Too many open files" is usually fixed by a restart and a config edit, without anyone asking whose limit ran out. The lesson takes the mechanism apart: two limits instead of one, inheritance at startup, and the fact that a descriptor is not a file but any open object — every network connection included.
Full technical treatment
TL;DR
A descriptor is a number a process uses to refer to something it has open, and the limit bounds how many such numbers one process may hold. There are two limits and they do different jobs: the kernel checks the soft one at the moment a process opens another descriptor, while the hard one bounds how high the process may raise its own soft limit. Both belong to a process and pass to its children.
Hence the consequence that matters: "I raised the limit" and "the service got
it" are different statements. A process raises its own soft limit with no
restart and no new config, but it starts with whatever limit the thing that
started it had: measured — the parent set itself 128 and the child started at
128. That is why ulimit -n in one shell changes nothing for a service started
from somewhere else. And the limit counts occupied numbers rather than files:
with a limit of 64, 61 opens succeeded, because three numbers came with the
process at birth.
Beyond that is what separates knowing from having read. The error is called
EMFILE and speaks of a process's limit; measured: the name EMFILE, the text
"Too many open files". The machine has a ceiling of its own, and exhausting it
raises ENFILE — a different error, fixed in a different place. A descriptor is
not only a file: at the same limit a socket gave 61, a pipe 60 (two ends, two
numbers) and an epoll instance 61, so every connection a database client or an
HTTP pool holds takes a number too. And lowering the hard limit is irreversible:
an unprivileged process may lower its own ceiling but not raise it back —
getrlimit(2) calls the lowering irreversible, and in the run an attempt to
raise the hard limit above its current value was refused.
- a program opens files and makes network connections;
- a service under load holds many of those connections at the same time;
- somebody starts the service: a shell, a service manager or a container.
RLIMIT_NOFILE,EMFILE,ENFILE,setrlimit;- what a soft and a hard limit are and how they differ;
epoll,inotify, theCLOSE_WAITstate.
What is actually being asked
The ladder usually runs like this:
- "What does 'Too many open files' mean?" — the warm-up.
- "Whose limit is it — the process's, the user's, the machine's?" — where the substance starts.
- "Soft and hard limit — what is the difference?" — whether you know that a process can change one of them itself.
- "What counts as a descriptor?" — whether you name sockets and
epollrather than only files. - "Why did raising the limit in a shell not help?" — a question about inheritance.
- "How do you tell a descriptor leak from an honest shortage?" — a question about what to watch.
The numbers come from running bench/rlimits/descriptors.py and
bench/rlimits/practice.py. There is not a single timing here — only
descriptor counts and error names — so everything reproduces on any Linux
machine except the values of the limits themselves, which are each machine's
own.
Base: a descriptor is a number, not a file
Before talking about limits it is worth naming, in ordinary words, what exactly is being counted.
A descriptor is a small whole number. When a process opens a file, connects to a database or creates a pipe between itself and a subprocess, the kernel sets up the object it needs and hands the process a number. From then on the process works with the number rather than with the object: it asks to read, to write and to close by that number.
Every process has its own numbering. The table of numbers belongs to the process: number 7 in one process and number 7 in another point at completely different things. That is why what is open is counted per process rather than per machine.
Three numbers are taken before the first line of your code. They are 0, 1 and 2 — input, output and the error stream; the process receives them at birth. You did not open them, but they are occupied exactly like everything else.
And here is the question the rest of the lesson answers: how many numbers is a process allowed to hold at once, and where does that allowance come from? There is always an allowance — no table is infinite. Every process has its own, and it reaches the process not from its own code but from whoever started it. When the numbers run out, the kernel refuses to open the next one, and that is where the familiar line about too many open files appears in the log.
That is already enough to answer the basic interview question. What follows is about why there are in fact two allowances, what other than files spends numbers, and why a limit raised in a shell so often never reaches the service.
Mechanism 1: two limits, and it is not duplication
getrlimit(2) defines the pair like this:
The soft limit is the value that the kernel enforces for the corresponding
resource. The hard limit acts as a ceiling for the soft limit: an unprivileged
process may set only its soft limit to a value in the range from 0 up to the
hard limit, and (irreversibly) lower its hard limit.
Three consequences, and all three get asked about.
The soft limit is the one checked. The kernel consults it at the moment a process opens another descriptor: not enough left, refusal. When someone says "the descriptor limit is a thousand", they almost always mean this one.
The hard limit bounds the raising, not the opening. How many descriptors a process opens is decided by the soft limit; how high the process may raise its own soft limit is decided by the hard one. And it may do that raising itself, without a restart. This is the most underused fix there is: a service that has hit its soft limit often needs neither a new config nor a restart, just one call at startup.
The hard limit only goes down. Lower it and it stays lowered unless the
process holds CAP_SYS_RESOURCE; that is a property of the process, not of a
setting.
The run shows all of it:
1. TWO LIMITS, AND ONLY ONE OF THEM IS ENFORCED
-----------------------------------------------
soft limit (the one the kernel enforces) 20000
hard limit (the ceiling for the soft one) 20000
descriptors this process holds right now 3
after lowering the soft limit to 256 256
raised back to the hard limit, no restart 20000
raising the hard limit refused: ValueError
Note the third line: the process already holds three descriptors — input, output and the error stream. They count too — and they are exactly why the next section arrives at 61 rather than 64.
Mechanism 2: EMFILE is about a process, not a machine
getrlimit(2) says plainly what happens when the limit is reached:
This specifies a value one greater than the maximum file descriptor number that
can be opened by this process. Attempts (open(2), pipe(2), dup(2), etc.) to
exceed this limit yield the error EMFILE.
The key words are this process. There is a system-wide ceiling as well, but
it raises a different error, and open(2) separates them explicitly: EMFILE
is "the per-process limit on the number of open file descriptors has been
reached", ENFILE is "the system-wide limit on the total number of open files
has been reached".
In practice that is the first thing to establish from a log: which of the two
letters. EMFILE means one process is at fault and the cure is at process
level. ENFILE means a shared resource of the machine ran out: proc(5) describes it
as /proc/sys/fs/file-max, the file that "defines a system-wide limit on the
number of open files for all processes". Editing one service's limit will not
help there.
Now the exhaustion itself. A child sets its soft limit to 64 and opens files until the kernel refuses:
2. WHAT HITTING THE LIMIT LOOKS LIKE
------------------------------------
soft limit set by the child 64
descriptors the child already had 3
files it managed to open 61
error name EMFILE
error message Too many open files
61, not 64. Three descriptors were already there, and they count against the limit. At 64 that looks like nitpicking; it stops looking like it when the subject is a connection pool, because the limit counts everything open, not everything you opened.
Mechanism 3: a descriptor is not a file
The word "file" in the name misleads. What counts is any object with a number in the process's descriptor table, and that can be checked by opening different things until refusal:
3. IT IS NOT ONLY FILES THAT COUNT
----------------------------------
file: opened before the refusal 61 — EMFILE: Too many open files
socket: opened before the refusal 61 — EMFILE: Too many open files
pipe: opened before the refusal 60 — EMFILE: Too many open files
epoll: opened before the refusal 61 — EMFILE: Too many open files
A socket, a pipe end, an epoll instance — each takes a number. Hence the
practical list of what eats the limit in a real service: database connections,
HTTP pool connections, listening sockets, accepted connections, inotify
watches, timers, open log files.
And one separate line about pipes: 60 rather than 61. There are 61 free numbers, an odd count, and a pipe takes two at once: thirty pipes occupy sixty numbers, and the single number left is not enough for a thirty-first. A detail, but it explains why the count of open objects and the count of occupied numbers diverge: a process that spawns subprocesses with captured output spends numbers in pairs.
Mechanism 4: the limit is inherited, hence "I raised it and nothing changed"
The practical mistake that makes a limit "not work" is usually one: it was
raised for the wrong process. The limit belongs to a process but passes to its
children, and execve(2) states this the other way round — "All process
attributes are preserved during an execve(), except the following" — with
resource limits absent from the list of exceptions.
4. THE LIMIT IS PER PROCESS, AND IT IS INHERITED
------------------------------------------------
soft limit set in the parent 128
soft limit the child starts with 128
soft limit restored in the parent 20000
Hence the rule worth answering with: the limit is set on whoever starts the
process. If a service manager starts the service, the limit belongs in the
service unit; if a container does, in the container's settings. ulimit -n in
your shell changes the limit only for what you start from that shell and means
nothing for a process started another way.
Check it where the kernel keeps it: /proc/<pid>/limits shows the limits in
force for that process. It is the only answer that does not depend on what
the configs say.
Deeper: a leak or an honest shortage
Both look identical — EMFILE in the log — and their cures are opposite. The
difference shows in one observation: does the number of open descriptors grow
monotonically?
You count it where the rest of this lesson looks: /proc/<pid>/fd holds an
entry per open descriptor, and their count is the current consumption. From
there, two cases.
The count grows and does not fall when load drops: a leak. Something is not being closed — a response, a connection, a file. A higher limit only postpones it.
The count sits on a plateau near the limit and falls with the load: an honest shortage. That many connections are genuinely needed. Then the limit is raised — and raised for whoever starts the process.
There is a third case that gets mistaken for a leak: sockets in CLOSE_WAIT.
The peer closed the connection and the application did not, so the descriptor
stays taken until close is called. It will not free itself: this is a leak
whose cause happens to be visible from outside.
How to answer in an interview
Short answer: EMFILE is a limit on the number of descriptors held by one
process, and what it counts is not files but every open object — sockets, pipes,
epoll — plus the three the process received at birth. There are two limits:
the kernel checks the soft one when a descriptor is opened, while the hard one
bounds how high the process may raise its own soft limit — and the process can
do that raising itself, without a restart.
That is enough for a correct answer. What follows is what you add when the interviewer digs.
If the interviewer digs deeper
Three things separate a good answer. First, you distinguish EMFILE from
ENFILE: one is about the process, the other about the machine, and they are
fixed in different places. Second, you talk about inheritance and know why
ulimit -n in somebody's shell changed nothing: the limit comes from whoever
starts the process. Third, you name how to tell a leak from a shortage — watch
the count in /proc/<pid>/fd over time, not the fact of the error.
Next they ask
Why does an application hit the limit when it opens a dozen files?
Because files are not the only thing that uses up descriptors. Measured: a socket, a pipe end
and an epoll instance take a number exactly as a file does. In a service, the
bulk of descriptors are connections: to the database, to neighbouring services,
and those accepted from clients.
So you count them not by reading the code but by reading /proc/<pid>/fd.
What is not in the code shows up there: pools inside client libraries, file
watches, timers.
Can the limit be raised without restarting the service?
The soft one, yes — that is exactly what the first block of the run shows: the process lowered its soft limit and raised it back to the hard one, restarting nothing. If the program makes that call at startup, "raise the limit" stops being an operation with downtime.
Above the hard limit, no: the attempt in the run was refused. And if somebody has lowered the hard limit, raising it back in the same process is not possible either — that is described as irreversible.
How does EMFILE differ from ENFILE, and why does it matter?
EMFILE is the limit of this process; ENFILE is the system-wide ceiling
on open files. Different errors, different ceilings, different cures: in the
first case you change the process's limit, in the second a system-wide setting,
and changing the wrong one achieves nothing.
The practical value of the distinction is that it is available immediately: the
error name is already in the log. If it says ENFILE, your service may not be
at fault at all — it was simply the one that came up short.
Does raising the limit to the maximum and forgetting it work?
A limit protects not only against you but against your neighbours: it bounds the
cost of somebody's mistake. Raised to the ceiling, it trades one service's
EMFILE for the whole machine's ENFILE: the system-wide ceiling in
/proc/sys/fs/file-max has not gone anywhere, and whoever runs into it will not
be the one at fault.
The useful move is different: raise the limit deliberately to a value matching
the expected number of connections, and watch the count in /proc/<pid>/fd.
Then EMFILE stops being a surprise and a rising count becomes an early signal.
Common misconceptions
'Too many open files' means the machine ran out of descriptors
It is a limit of the process: EMFILE is "the per-process limit on the number of open file descriptors has been reached". A system-wide shortage raises a different error, ENFILE, fixed in a different place. The first thing to establish from a log is which of the two letters it is.
there is one limit and an administrator changes it
There are two, and they do different jobs. The kernel checks the soft one at the moment a process opens another descriptor; the hard one bounds how high the process may raise its own soft limit. Raising the soft one up to the hard one, a process does itself and without a restart — measured in the first block of the run. An administrator is needed only when the hard limit is too low.
with a limit of 64 you can open 64 files
Measured: 61. Three descriptors come with the process — input, output and the error stream — and they count against the limit. The limit counts occupied numbers, not the open calls you made.
only files use up descriptors
Measured at the same limit: a socket — 61, a pipe — 60, an epoll instance — 61. In a service the bulk of descriptors goes to connections, and a pipe takes two numbers at once, because it has two ends.
I raised the limit with ulimit -n, so the service has it
Only if the service was started from that very shell: the limit is inherited from whoever starts the process. Measured: the parent set itself 128 and the child started at 128. A service started by a service manager knows nothing of your ulimit; check it in /proc/<pid>/limits.
raise the limit higher and the problem is solved
Solved if it was an honest shortage: the count sits on a plateau and falls with the load. If it climbs monotonically and never returns, it is a leak, and a higher limit only postpones it. The two are distinguished not by the error text but by the count in /proc/<pid>/fd over time.
the hard limit can be lowered and then restored
It cannot: lowering the hard limit is described as irreversible for an unprivileged process. In the run, an attempt to raise it above its current value was refused. The ceiling comes back only with a new process that inherits it from a parent.
Practice
Two exercises. Answer first, then check against the real output: in both, the correct answer comes from a script's committed output rather than being written by hand.
Practice · predict the output
name, message, opened, held = probe(SOFT) print(name) print(message) print(inherited_soft(128))
Practice · estimate
Knowledge check
A service log says 'Too many open files'. What does that tell you about the limit?
This is neither a retelling nor a separate text: everything below is taken from the article itself — its own summary, the section headings, the “actually” column and the version table. Which is why these theses cannot drift from the article.
The gist
- A descriptor is a number a process uses to refer to something it has open, and the limit bounds how many such numbers one process may hold. There are two limits and they do different jobs: the kernel checks the soft one at the moment a process opens another descriptor, while the hard one bounds how high the process may raise its own soft limit. Both belong to a process and pass to its children.
- Hence the consequence that matters: "I raised the limit" and "the service got it" are different statements. A process raises its own soft limit with no restart and no new config, but it starts with whatever limit the thing that started it had: measured — the parent set itself 128 and the child started at 128. That is why
ulimit -nin one shell changes nothing for a service started from somewhere else. And the limit counts occupied numbers rather than files: with a limit of 64, 61 opens succeeded, because three numbers came with the process at birth. - Beyond that is what separates knowing from having read. The error is called
EMFILEand speaks of a process's limit; measured: the nameEMFILE, the text "Too many open files". The machine has a ceiling of its own, and exhausting it raisesENFILE— a different error, fixed in a different place. A descriptor is not only a file: at the same limit a socket gave 61, a pipe 60 (two ends, two numbers) and anepollinstance 61, so every connection a database client or an HTTP pool holds takes a number too. And lowering the hard limit is irreversible: an unprivileged process may lower its own ceiling but not raise it back —getrlimit(2)calls the lowering irreversible, and in the run an attempt to raise the hard limit above its current value was refused.
In fact
- It is a limit of the process:
EMFILEis "the per-process limit on the number of open file descriptors has been reached". A system-wide shortage raises a different error,ENFILE, fixed in a different place. The first thing to establish from a log is which of the two letters it is. - There are two, and they do different jobs. The kernel checks the soft one at the moment a process opens another descriptor; the hard one bounds how high the process may raise its own soft limit. Raising the soft one up to the hard one, a process does itself and without a restart — measured in the first block of the run. An administrator is needed only when the hard limit is too low.
- Measured: 61. Three descriptors come with the process — input, output and the error stream — and they count against the limit. The limit counts occupied numbers, not the
opencalls you made. - Measured at the same limit: a socket — 61, a pipe — 60, an
epollinstance — 61. In a service the bulk of descriptors goes to connections, and a pipe takes two numbers at once, because it has two ends. - Only if the service was started from that very shell: the limit is inherited from whoever starts the process. Measured: the parent set itself 128 and the child started at 128. A service started by a service manager knows nothing of your
ulimit; check it in/proc/<pid>/limits. - Solved if it was an honest shortage: the count sits on a plateau and falls with the load. If it climbs monotonically and never returns, it is a leak, and a higher limit only postpones it. The two are distinguished not by the error text but by the count in
/proc/<pid>/fdover time. - It cannot: lowering the hard limit is described as irreversible for an unprivileged process. In the run, an attempt to raise it above its current value was refused. The ceiling comes back only with a new process that inherits it from a parent.
What is covered
- What is actually being asked
- Base: a descriptor is a number, not a file
- Mechanism 1: two limits, and it is not duplication
- Mechanism 2: EMFILE is about a process, not a machine
- Mechanism 3: a descriptor is not a file
- Mechanism 4: the limit is inherited, hence "I raised it and nothing changed"
- Deeper: a leak or an honest shortage
- How to answer in an interview
- Next they ask
- Common misconceptions
- Practice
- Knowledge check
Sources & further reading
4 SOURCES
- getrlimit(2), Linux man-pages 6.7Official documentation. Where the two limits come from and who may change them: "The soft limit is the value that the kernel enforces for the corresponding resource. The hard limit acts as a ceiling for the soft limit: an unprivileged process may set only its soft limit to a value in the range from 0 up to the hard limit, and (irreversibly) lower its hard limit". And what RLIMIT_NOFILE bounds: "This specifies a value one greater than the maximum file descriptor number that can be opened by this process. Attempts (open(2), pipe(2), dup(2), etc.) to exceed this limit yield the error EMFILE".https://man7.org/linux/man-pages/man2/getrlimit.2.html
- open(2), Linux man-pages 6.7Official documentation. The distinction half the lesson rests on: EMFILE is "The per-process limit on the number of open file descriptors has been reached", ENFILE is "The system-wide limit on the total number of open files has been reached". Different errors, different ceilings, different cures.https://man7.org/linux/man-pages/man2/open.2.html
- execve(2), Linux man-pages 6.7Official documentation. Why a child inherits the limit — the rule is stated the other way round: "All process attributes are preserved during an execve(), except the following", and resource limits are not in the list of exceptions. Hence the practical rule of the lesson: the limit is set on whoever starts the service, not on the service.https://man7.org/linux/man-pages/man2/execve.2.html
- proc(5), Linux man-pages 6.7Official documentation. Where to look instead of guessing. The count of open descriptors: "This is a subdirectory containing one entry for each file which the process has open, named by its file descriptor". The limits in force for a process: "This file displays the soft limit, hard limit, and units of measurement for each of the process's resource limits". And the system-wide ceiling behind ENFILE: "This file defines a system-wide limit on the number of open files for all processes. System calls that fail when encountering this limit fail with the error ENFILE".https://man7.org/linux/man-pages/man5/proc.5.html