Process Resource Limits

Each process has a set of resource limits that can be used to restrict the amounts of various system resources that the process may consume. For example, we may want to set resource limits on a process before execing an arbitrary program, if we are concerned that it may consume excessive resources. We can set the resource limits of the shell using the ulimit built-in command (limit in the C shell). These limits are inherited by the processes that the shell creates to execute user commands.

Note

Since kernel 2.6.24, the Linux-specific /proc/PID/limits file can be used to view all of the resource limits of any process. This file is owned by the real user ID of the corresponding process and its permissions allow reading only by that user ID (or by a privileged process).

The getrlimit() and setrlimit() system calls allow a process to fetch and modify its resource limits.

#include <sys/resource.h>

int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);

Note

Both return 0 on success, or -1 on error

The resource argument identifies the resource limit to be retrieved or changed. The rlim argument is used to return resource limit values (getrlimit()) or to specify new resource limit values (setrlimit()), and is a pointer to a structure containing two fields:

struct rlimit {
    rlim_t rlim_cur;        /* Soft limit (actual process limit) */
    rlim_t rlim_max;        /* Hard limit (ceiling for rlim_cur) */
};

These fields correspond to the two associated limits for a resource: the soft (rlim_cur) and hard (rlim_max) limits. (The rlim_t data type is an integer type.) The soft limit governs the amount of the resource that may be consumed by the process. A process can adjust the soft limit to any value from 0 up to the hard limit. For most resources, the sole purpose of the hard limit is to provide this ceiling for the soft limit. A privileged (CAP_SYS_RESOURCE) process can adjust the hard limit in either direction (as long as its value remains greater than the soft limit), but an unprivileged process can adjust the hard limit only to a lower value (irreversibly). The value RLIM_INFINITY in rlim_cur or rlim_max means infinity (no limit on the resource), both when retrieved via getrlimit() and when set via setrlimit().

In most cases, resource limits are enforced for both privileged and unprivileged processes. They are inherited by child processes created via fork() and are preserved across an exec().

The values that can be specified for the resource argument of getrlimit() and setrlimit() are summarized in Table 36-1 and detailed in Section 36.3.

Although a resource limit is a per-process attribute, in some cases, the limit is measured against not just that process’s consumption of the corresponding resource, but also against the sum of resources consumed by all processes with the same real user ID. The RLIMIT_NPROC limit, which places a limit on the number of processes that can be created, is a good example of the rationale for this approach. Applying this limit against only the number of children that the process itself created would be ineffective, since each child that the process created would also be able to create further children, which could create more children, and so on. Instead, the limit is measured against the count of all processes that have the same real user ID. Note, however, that the resource limit is checked only in the processes where it has been set (i.e., the process itself and its descendants, which inherit the limit). If another process owned by the same real user ID has not set the limit (i.e., the limit is infinite) or has set a different limit, then that process’s capacity to create children will be checked according to the limit that it has set.

As we describe each resource limit below, we note those limits that are measured against the resources consumed by all processes with the same real user ID. If not otherwise specified, then a resource limit is measured only against the process’s own consumption of the resource.

Note

Be aware that, in many cases, the shell commands for getting and setting resource limits (ulimit in bash and the Korn shell, and limit in the C shell) use different units from those used in getrlimit() and setrlimit(). For example, the shell commands typically express the limits on the size of various memory segments in kilobytes.

Table 36-1. Resource values for getrlimit() and setrlimit()

resource

Limit on

SUSv3

RLIMIT_AS

Process virtual memory size (bytes)

RLIMIT_CORE

Core file size (bytes)

RLIMIT_CPU

CPU time (seconds)

RLIMIT_DATA

Process data segment (bytes)

RLIMIT_FSIZE

File size (bytes)

RLIMIT_MEMLOCK

Locked memory (bytes)

 

RLIMIT_MSGQUEUE

Bytes allocated for POSIX message queues for real user ID (since Linux 2.6.8)

 

RLIMIT_NICE

Nice value (since Linux 2.6.12)

 

RLIMIT_NOFILE

Maximum file descriptor number plus one

RLIMIT_NPROC

Number of processes for real user ID

 

RLIMIT_RSS

Resident set size (bytes; not implemented)

 

RLIMIT_RTPRIO

Realtime scheduling priority (since Linux 2.6.12)

 

RLIMIT_RTTIME

Realtime CPU time (microseconds; since Linux 2.6.25)

 

RLIMIT_SIGPENDING

Number of queued signals for real user ID (since Linux 2.6.8)

 

RLIMIT_STACK

Size of stack segment (bytes)

Before going into the specifics of each resource limit, we look at a simple example of the use of resource limits. Example 36-2 defines the function printRlimit(), which displays a message, along with the soft and hard limits for a specified resource.

The program in Example 36-3 calls setrlimit() to set the soft and hard limits on the number of processes that a user may create (RLIMIT_NPROC), uses the printRlimit() function of Example 36-2 to display the limits before and after the change, and then creates as many processes as possible. When we run this program, setting the soft limit to 30 and the hard limit to 100, we see the following:

$ ./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
ERROR [EAGAIN Resource temporarily unavailable] fork

In this example, the program managed to create only 4 new processes, because 26 processes were already running for this user.

In some programming environments, the rlim_t data type may not be able to represent the full range of values that could be maintained for a particular resource limit. This may be the case on a system that offers multiple programming environments in which the size of the rlim_t data type differs. Such systems can arise if a large-file compilation environment with a 64-bit off_t is added to a system on which off_t was traditionally 32 bits. (In each environment, rlim_t would be the same size as off_t.) This leads to the situation where a program with a small rlim_t can, after being execed by a program with a 64-bit off_t, inherit a resource limit (e.g., the file size limit) that is greater than the maximum rlim_t value.

To assist portable applications in handling the possibility that a resource limit may be unrepresentable, SUSv3 specifies two constants to indicate unrepresentable limit values: RLIM_SAVED_CUR and RLIM_SAVED_MAX. If a soft resource limit can’t be represented in rlim_t, then getrlimit() will return RLIM_SAVED_CUR in the rlim_cur field. RLIM_SAVED_MAX performs an analogous function for an unrepresentable hard limit returned in the rlim_max field.

If all possible resource limit values can be represented in rlim_t, then SUSv3 permits an implementation to define RLIM_SAVED_CUR and RLIM_SAVED_MAX to be the same as RLIM_INFINITY. This is how these constants are defined on Linux, implying that all possible resource limit values can be represented in rlim_t. However, this is not the case on 32-bit architectures such as x86-32. On those architectures, in a large-file compilation environment (i.e., setting the _FILE_OFFSET_BITS feature test macro to 64 as described in I/O on Large Files), the glibc definition of rlim_t is 64 bits wide, but the kernel data type for representing a resource limit is unsigned long, which is only 32 bits wide. Current versions of glibc deal with this situation as follows: if a program compiled with _FILE_OFFSET_BITS=64 tries to set a resource limit to a value larger than can be represented in a 32-bit unsigned long, then the glibc wrapper for setrlimit() silently converts the value to RLIM_INFINITY. In other words, the requested setting of the resource limit is not honored.

Note

Because utilities that handle files are normally compiled with _FILE_OFFSET_BITS=64 in many x86-32 distributions, the failure to honor resource limits larger than the value that can be represented in 32 bits is a problem that can affect not only application programmers, but also end users.

One could argue that it might be better for the glibc setrlimit() wrapper to give an error if the requested resource limit exceeds the capacity of a 32-bit unsigned long. However, the fundamental problem is a kernel limitation, and the behavior described in the main text is the approach that the glibc developers have taken to dealing with it.