What is RLIMIT_NPROC?
RLIMIT_NPROC limits the maximum number of processes (and threads) that can exist for the real user ID of the calling process. It is BSD-derived, not in SUSv3, and available on Linux and the BSDs.
Note: despite the name “NPROC”, this limit actually counts threads, not just processes. Threads are created with clone() and each counts against RLIMIT_NPROC just like a process created with fork().
Which Calls Are Affected
When the real-user-ID process count is at or above the soft RLIMIT_NPROC limit, these calls fail with EAGAIN:
This limit is not enforced for processes with CAP_SYS_ADMIN or CAP_SYS_RESOURCE capabilities.
Per-User-ID Scope
RLIMIT_NPROC counts all processes with the same real user ID, system-wide — not just the children of the current process. This is why in the textbook example, setting the limit to 30 but having 26 pre-existing processes meant only 4 more could be created.
The limit is only checked in processes that have set it or inherited it. Another process owned by the same user that has RLIMIT_NPROC set to RLIM_INFINITY is not constrained.
System-Wide Thread Limit: /proc/sys/kernel/threads-max
Independently of RLIMIT_NPROC, Linux imposes a system-wide cap on the total number of threads (processes) across all users. This limit is in /proc/sys/kernel/threads-max (Linux 2.4+). Even root cannot exceed this. The default value is computed at boot based on available physical memory.
# View system-wide thread limit
$ cat /proc/sys/kernel/threads-max
62689
# Raise it (requires root)
# echo 100000 > /proc/sys/kernel/threads-max
Finding Current Process Count for a User ID
There is no direct system call to query “how many processes does user X currently have?” The portable workaround on Linux is to scan /proc/PID/status files and look at the Uid field:
# The Uid line in /proc/PID/status format:
# Uid: real effective saved-set filesystem
$ grep "^Uid:" /proc/*/status 2>/dev/null | awk '{print $2}' | sort | uniq -c
# Shows count of processes per real UID
# Or: count processes for the current user
$ ps -u $(id -u) --no-headers | wc -l
Code Example — RLIMIT_NPROC with fork() Loop
#include
#include
#include
#include
#include <sys/resource.h>
#include <sys/wait.h>
int main(void)
{
struct rlimit rl;
pid_t pid;
int children = 0;
/* Set RLIMIT_NPROC: soft=5 extra processes, hard=10 */
getrlimit(RLIMIT_NPROC, &rl);
printf("Current NPROC: soft=%lld hard=%lld\n",
(long long)rl.rlim_cur, (long long)rl.rlim_max);
/* Lower soft to current_count + 3 so we can create only ~3 more */
rl.rlim_cur = 10; /* allow 10 total for this user */
rl.rlim_max = 20;
setrlimit(RLIMIT_NPROC, &rl);
/* Fork in a loop until EAGAIN */
while (1) {
pid = fork();
if (pid == -1) {
if (errno == EAGAIN) {
printf("fork() EAGAIN: RLIMIT_NPROC hit after %d children\n",
children);
break;
}
perror("fork"); break;
}
if (pid == 0) {
sleep(5); /* Child: stay alive briefly */
_exit(0);
}
children++;
printf(" Created child %d (PID %d)\n", children, pid);
}
/* Clean up */
while (wait(NULL) != -1) ;
return 0;
}
/* Compile: gcc -o nproc_demo nproc_demo.c
Note: actual number of children depends on pre-existing processes */
Interview Questions
On Linux, threads are created with clone() just like processes. The kernel sees them as lightweight processes sharing resources. RLIMIT_NPROC is enforced by clone(), so every thread creation also counts against this limit. The name predates the widespread use of POSIX threads but the implementation counts both.
EAGAIN (Resource temporarily unavailable). The same errno value is returned by vfork() and clone(). It is “temporary” because the limit could be freed if other processes with the same user ID exit.
sysconf(_SC_CHILD_MAX). On Linux, before 2.6.23, this always returned 999 (inaccurate). Since 2.6.23 with glibc 2.4+, it correctly returns the RLIMIT_NPROC soft limit value.
No. RLIMIT_NPROC is not enforced for processes with CAP_SYS_ADMIN or CAP_SYS_RESOURCE capabilities. However, the system-wide threads-max limit in /proc/sys/kernel/threads-max applies to everyone including root.
