Example Programs printRlimit() and rlimit_nproc

 

36.2c — Example Programs
printRlimit() and rlimit_nproc (Listings 36-2 & 36-3) | EmbeddedPathashala

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            */
Why cast to long long? rlim_t is the same size as off_t. In a large-file compilation environment on a 32-bit system, rlim_t is 64 bits wide while rlim_t values in the kernel are only 32 bits. Casting to long long and using %lld is the safe, portable approach across all compilation environments.

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.
 */
Key insight: The program created only 4 new processes even with a limit of 30 because 26 processes were already running for that user. RLIMIT_NPROC counts all processes with the same real user ID — not just the ones this program created.

Interview Questions

Q1. In the printRlimit() function, why does it check RLIM_SAVED_CUR in addition to RLIM_INFINITY?

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.

Q2. In the rlimit_nproc demo, why does the program create fewer children than the soft limit it set?

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.

Q3. What error does fork() return when RLIMIT_NPROC is exceeded?

fork() fails and sets errno to EAGAIN (Resource temporarily unavailable). The same error is returned by vfork() and clone() when RLIMIT_NPROC is exceeded.

Q4. Why are child processes not waited for in the rlimit_nproc demo?

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.

Leave a Reply

Your email address will not be published. Required fields are marked *