What is RLIMIT_CPU?
RLIMIT_CPU limits the total amount of CPU time (in seconds, combining both user-mode and kernel-mode execution) that a process may consume. This is a powerful resource limit for preventing runaway computation — infinite loops, recursive explosions, or heavy scientific calculations that should not run forever.
This limit is specified by SUSv3 and is widely available across all UNIX systems.
How Enforcement Works — Two Stages
RLIMIT_CPU enforcement is signal-based and has two distinct stages corresponding to the soft and hard limits:
When the process has consumed CPU time equal to the soft RLIMIT_CPU limit, the kernel sends it a SIGXCPU signal. The default action for SIGXCPU is to terminate the process with a core dump. On Linux, if the process catches SIGXCPU and returns, the kernel sends another SIGXCPU for each additional second of CPU time consumed.
If the process continues consuming CPU time past the hard RLIMIT_CPU limit, the kernel sends a SIGKILL signal, which cannot be caught or ignored. The process is unconditionally terminated.
SIGXCPU Signal Handling Options
When a process receives SIGXCPU, it has three options:
- Default action (terminate + core dump) — the process dies when the soft limit is hit. Suitable for most cases.
- Catch SIGXCPU and do cleanup — install a handler that saves state, closes files, and exits gracefully. On Linux, the handler will be called again each second until the hard limit is hit and SIGKILL terminates it.
- Catch SIGXCPU and raise the limit — inside the handler, call setrlimit() to increase RLIMIT_CPU and continue. Only works if the process has sufficient privilege to raise the limit, or if the new limit is within the hard limit.
Code Example — RLIMIT_CPU with SIGXCPU Handler
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/resource.h>
static void sigxcpu_handler(int sig)
{
/* Called each second after soft CPU limit is hit */
printf("\nSIGXCPU received! Doing cleanup and exiting.\n");
exit(1); /* Exit before hard limit causes SIGKILL */
}
int main(void)
{
struct rlimit rl;
/* Install handler for SIGXCPU */
signal(SIGXCPU, sigxcpu_handler);
/* Set soft CPU limit to 2 seconds, hard to 5 seconds */
rl.rlim_cur = 2; /* soft: send SIGXCPU after 2 sec */
rl.rlim_max = 5; /* hard: SIGKILL after 5 sec */
if (setrlimit(RLIMIT_CPU, &rl) == -1) {
perror("setrlimit"); exit(1);
}
printf("CPU limit: soft=2s hard=5s. Running infinite loop...\n");
/* Infinite loop — will eventually hit the CPU limit */
volatile long i = 0;
while (1) {
i++; /* Burns CPU in user mode */
}
return 0; /* Never reached */
}
/* Compile: gcc -O0 -o rlimit_cpu rlimit_cpu.c
Run: ./rlimit_cpu
Expected: after ~2 seconds of CPU time, SIGXCPU handler fires. */
Interview Questions
SIGXCPU is sent. The default action is to terminate the process with a core dump. If the process catches SIGXCPU and returns, Linux sends another SIGXCPU for each additional second of CPU time consumed, until the hard limit is hit.
SIGKILL is sent. This signal cannot be caught, blocked, or ignored. The process is unconditionally terminated by the kernel. This is the final enforcement backstop after SIGXCPU signals have been delivered.
CPU time (total time the process was actually running on a CPU, both user-mode and kernel-mode). Wall-clock time is different — a process sleeping for 10 minutes consumes near-zero CPU time even though 10 minutes pass. RLIMIT_CPU only counts time the process was actually executing.
For portable code, the SIGXCPU handler should do required cleanup and exit on first receipt. UNIX systems vary in how they deliver subsequent SIGXCPU signals. On Linux they come once per second until the hard limit; on some other systems only one SIGXCPU is sent. Relying on repeated signals is not portable.
