Two Example Programs
Before diving into each individual resource limit, the textbook presents two example programs that demonstrate how to read and set resource limits in practice. Listing 36-2 defines a reusable helper function printRlimit(). Listing 36-3 uses it to show RLIMIT_NPROC limits before and after modification, then creates child processes until the limit is hit.
Listing 36-2 — The printRlimit() Helper
This function takes a message string and a resource constant, reads the current limits, and prints them. It handles three cases for each limit: RLIM_INFINITY (print “infinite”), RLIM_SAVED_CUR/MAX (print “unrepresentable”), or a normal numeric value cast to long long.
#include <stdio.h>
#include <sys/resource.h>
int printRlimit(const char *msg, int resource)
{
struct rlimit rlim;
if (getrlimit(resource, &rlim) == -1)
return -1;
printf("%s soft=", msg);
if (rlim.rlim_cur == RLIM_INFINITY)
printf("infinite");
#ifdef RLIM_SAVED_CUR /* Not defined on all implementations */
else if (rlim.rlim_cur == RLIM_SAVED_CUR)
printf("unrepresentable");
#endif
else
printf("%lld", (long long)rlim.rlim_cur);
printf("; hard=");
if (rlim.rlim_max == RLIM_INFINITY)
printf("infinite\n");
#ifdef RLIM_SAVED_MAX
else if (rlim.rlim_max == RLIM_SAVED_MAX)
printf("unrepresentable");
#endif
else
printf("%lld\n", (long long)rlim.rlim_max);
return 0;
}
/* Usage: printRlimit("Max processes: ", RLIMIT_NPROC);
Output: Max processes: soft=1024; hard=1024 */
Listing 36-3 — RLIMIT_NPROC Demo
This program takes a soft limit and optional hard limit from command-line arguments, sets RLIMIT_NPROC, then creates child processes in a loop until fork() fails. It demonstrates hitting the limit in practice.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/resource.h>
/* Run: ./rlimit_nproc 30 100
Sets soft=30, hard=100 then forks until EAGAIN */
int main(int argc, char *argv[])
{
struct rlimit rl;
int j;
pid_t childPid;
if (argc < 2 || argc > 3) {
fprintf(stderr, "Usage: %s soft-limit [hard-limit]\n", argv[0]);
exit(1);
}
/* Show current limits */
printRlimit("Initial maximum process limits: ", RLIMIT_NPROC);
/* Parse arguments: 'i' means RLIM_INFINITY */
rl.rlim_cur = (argv[1][0] == 'i') ? RLIM_INFINITY : atol(argv[1]);
rl.rlim_max = (argc == 2) ? rl.rlim_cur :
(argv[2][0] == 'i') ? RLIM_INFINITY : atol(argv[2]);
if (setrlimit(RLIMIT_NPROC, &rl) == -1) {
perror("setrlimit"); exit(1);
}
printRlimit("New maximum process limits: ", RLIMIT_NPROC);
/* Fork until limit is hit */
for (j = 1; ; j++) {
switch (childPid = fork()) {
case -1:
perror("fork"); /* Will print EAGAIN when limit hit */
exit(1);
case 0:
_exit(EXIT_SUCCESS); /* Child exits immediately */
default:
/* Parent: note each new child, leave zombie to count */
printf("Child %d (PID=%ld) started\n", j, (long)childPid);
break;
}
}
}
/*
* Sample run: ./rlimit_nproc 30 100
*
* Initial maximum process limits: soft=1024; hard=1024
* New maximum process limits: soft=30; hard=100
* Child 1 (PID=15674) started
* Child 2 (PID=15675) started
* Child 3 (PID=15676) started
* Child 4 (PID=15677) started
* fork: Resource temporarily unavailable (EAGAIN)
*
* Only 4 children created because 26 processes
* were already running under this user ID.
*/
Interview Questions
RLIM_SAVED_CUR is a constant the system returns when a resource limit value cannot be represented in the current environment’s rlim_t. This can happen on 32-bit systems in a large-file compilation environment where the kernel’s limit exceeds the maximum 32-bit value. The function checks this case to print “unrepresentable” rather than printing a garbage number.
RLIMIT_NPROC counts all processes with the same real user ID system-wide, not just the children of this process. If the user already has 26 other processes running and the limit is set to 30, then only 4 more can be created. The limit was hit because of pre-existing processes belonging to that user.
fork() fails and sets errno to EAGAIN (Resource temporarily unavailable). The same error is returned by vfork() and clone() when RLIMIT_NPROC is exceeded.
The demo intentionally lets children accumulate as zombies. Zombies are terminated processes that still have an entry in the process table — they still count toward RLIMIT_NPROC. If the parent called wait() and reaped each child immediately, new children could be created in their place and the limit would never be reached in the demo. The zombies are kept to maintain the process count and reach the limit sooner.
