Process Priorities: Nice Values

 

Process Priorities: Nice Values
Chapter 35.1 — How the Linux kernel scheduler uses nice values to favor or disfavor processes

← Chapter 35 Index  /  Process Priorities: Nice Values

What Is Process Scheduling?

When you run multiple programs at the same time on Linux — a browser, a terminal, a music player — they all share the same CPU(s). But the CPU can only execute one instruction at a time per core. The Linux kernel has a component called the scheduler that continuously decides which process should run next.

The default model Linux uses is called round-robin time-sharing. Think of it like a teacher in a classroom giving each student exactly 30 seconds to speak before moving to the next student. Each process gets a short burst of CPU time — called a time slice or quantum — and then the next process gets its turn.

Process A
Time Slice
Process B
Time Slice
Process C
Time Slice
Process A
Again
Process B
Again

Repeats
Round-Robin: Each process gets equal time on the CPU in turn

This satisfies two important requirements:

⚖️ Fairness

Every process gets a share of the CPU. No process is permanently ignored.

⚡ Responsiveness

A process does not wait too long before it gets CPU time again.

What Is the Nice Value?

Even in a round-robin system, sometimes you want one process to get more CPU time than others — for example, a video encoder is more important than a background log archiver. Linux provides the nice value for this purpose.

The nice value is a number that belongs to each process. It ranges from -20 (highest priority — gets more CPU) to +19 (lowest priority — gets less CPU). The default nice value for any new process is 0.

The name “nice” comes from the idea of being polite: a process that sets itself to a high nice value (+19) is being “nice” to other processes by voluntarily asking for less CPU time.

-20
Highest Priority
-10
0
Default
+10
+19
Lowest Priority
← More CPU Time (Privileged only)Less CPU Time →
🔒 Important Rule: On traditional UNIX (and Linux by default), only privileged processes (root or CAP_SYS_NICE) can set a negative nice value (raise priority). Unprivileged processes can only increase their nice value (lower their own priority). This change is irreversible for unprivileged users.
📌 Inheritance: The nice value is inherited by child processes created with fork() and preserved across exec(). So if a shell has nice value +10, its child processes also start at +10.
⚖️ Not a strict hierarchy: The nice value is a weighting factor, not an absolute ceiling. Even a process with nice value +19 still gets some CPU time — it is never completely starved. Since kernel 2.6.23, the differences between nice values have a much stronger effect, meaning low-priority processes get noticeably less CPU.

getpriority() and setpriority()

These two system calls let you read and change the nice value of a process, process group, or all processes belonging to a user.


#include <sys/resource.h>

/* Returns the nice value (possibly negative) of the specified process,
   or -1 on error */
int getpriority(int which, id_t who);

/* Sets the nice value. Returns 0 on success, -1 on error */
int setpriority(int which, id_t who, int prio);
    

The ‘which’ Argument

The which argument tells the kernel what kind of entity you are referring to:

which value Meaning of ‘who’ who = 0 means
PRIO_PROCESS A single process ID The calling process itself
PRIO_PGRP A process group ID The calling process’s group
PRIO_USER A real user ID The calling process’s real UID
⚠️ Error Detection Trick: getpriority() can legitimately return -1 as a valid nice value (when priority is -1). To detect an actual error, you must set errno = 0 before the call and check both the return value and errno afterwards.

RLIMIT_NICE: Allowing Unprivileged Priority Raises

Since kernel 2.6.12, Linux added RLIMIT_NICE. This resource limit allows unprivileged processes to raise their own priority (lower nice value) up to a certain ceiling. The formula is:

Maximum nice value = 20 − rlim_cur (RLIMIT_NICE soft limit)

Example: if RLIMIT_NICE soft limit = 25, then the process can raise its nice value up to 20 − 25 = -5. The useful range of RLIMIT_NICE is 1 (low) to 40 (high).

The Legacy nice() Function

Before setpriority() was standardized, there was a simpler function called nice(). It just adds an increment to the calling process’s current nice value.


#include <unistd.h>

/* Adds 'incr' to the calling process's nice value.
   Returns the new nice value on success, or -1 on error. */
int nice(int incr);
    

nice() is still available but superseded by setpriority() because:

  • setpriority() can target other processes, process groups, and users
  • setpriority() sets an absolute value instead of a relative increment
  • setpriority() is part of the POSIX standard

The command-line tools nice(1) and renice(8) are the shell equivalents of these system calls.

💻 Code Example 1: Get and Set Nice Value

This program reads the nice value of the current process, changes it, and reads it again.


/* nice_demo.c
 * Demonstrates getpriority() and setpriority() on the calling process.
 * Compile: gcc nice_demo.c -o nice_demo
 * Run: ./nice_demo       (normal user: can only raise nice value)
 *      sudo ./nice_demo  (root: can lower nice value too)
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/resource.h>

int main(void)
{
    int current_nice;
    int new_nice;

    /* Step 1: Read current nice value.
     * IMPORTANT: set errno = 0 before calling getpriority()
     * because it can return -1 as a valid (non-error) result.
     */
    errno = 0;
    current_nice = getpriority(PRIO_PROCESS, 0);  /* 0 = calling process */
    if (current_nice == -1 && errno != 0) {
        perror("getpriority");
        exit(EXIT_FAILURE);
    }
    printf("Current nice value: %d\n", current_nice);

    /* Step 2: Set a new nice value.
     * Unprivileged users can only increase (worsen) their nice value.
     * Root can set any value from -20 to +19.
     */
    if (setpriority(PRIO_PROCESS, 0, 10) == -1) {
        perror("setpriority (trying to set to 10)");
        exit(EXIT_FAILURE);
    }
    printf("Successfully set nice value to 10\n");

    /* Step 3: Read back to confirm */
    errno = 0;
    new_nice = getpriority(PRIO_PROCESS, 0);
    if (new_nice == -1 && errno != 0) {
        perror("getpriority after set");
        exit(EXIT_FAILURE);
    }
    printf("New nice value confirmed: %d\n", new_nice);

    /* Step 4: Try to set a high priority (needs root) */
    if (setpriority(PRIO_PROCESS, 0, -10) == -1) {
        perror("setpriority (trying -10, needs root)");
        /* Not a fatal error — just informational */
    } else {
        errno = 0;
        new_nice = getpriority(PRIO_PROCESS, 0);
        if (new_nice == -1 && errno != 0) {
            perror("getpriority");
        } else {
            printf("Raised priority to nice value: %d (you are root!)\n", new_nice);
        }
    }

    return EXIT_SUCCESS;
}
    
Expected output (normal user):
Current nice value: 0
Successfully set nice value to 10
New nice value confirmed: 10
setpriority (trying -10, needs root): Permission denied

💻 Code Example 2: Change Priority of Another Process

This example shows how to read and change the priority of a process given its PID on the command line.


/* t_setpriority.c
 * Usage: ./t_setpriority <which> <who> <prio>
 *   which: p = process, g = process group, u = user
 *   who:   PID / PGID / UID  (0 = calling process/group/user)
 *   prio:  nice value to set (-20 to +19)
 *
 * Example: ./t_setpriority p 1234 5
 *          Sets process 1234's nice value to 5.
 *
 * Compile: gcc t_setpriority.c -o t_setpriority
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/resource.h>

int main(int argc, char *argv[])
{
    int which, prio;
    id_t who;

    if (argc != 4) {
        fprintf(stderr, "Usage: %s [p|g|u] <who> <prio>\n", argv[0]);
        fprintf(stderr, "  p = process, g = process group, u = user\n");
        exit(EXIT_FAILURE);
    }

    /* Parse the 'which' argument */
    switch (argv[1][0]) {
    case 'p': which = PRIO_PROCESS; break;
    case 'g': which = PRIO_PGRP;   break;
    case 'u': which = PRIO_USER;   break;
    default:
        fprintf(stderr, "Invalid which: use p, g, or u\n");
        exit(EXIT_FAILURE);
    }

    who  = (id_t) atoi(argv[2]);
    prio = atoi(argv[3]);

    printf("Setting nice value for %s %d to %d...\n",
           (which == PRIO_PROCESS) ? "PID" :
           (which == PRIO_PGRP)    ? "PGID" : "UID",
           (int)who, prio);

    /* Attempts to set outside range [-20, +19] are silently clamped */
    if (setpriority(which, who, prio) == -1) {
        perror("setpriority");
        exit(EXIT_FAILURE);
    }

    /* Read back to verify — must guard against -1 being valid */
    errno = 0;
    prio = getpriority(which, who);
    if (prio == -1 && errno != 0) {
        perror("getpriority");
        exit(EXIT_FAILURE);
    }

    /* Note: if which is PRIO_PGRP or PRIO_USER, getpriority()
     * returns the highest priority (lowest numeric nice value)
     * among all matching processes.
     */
    printf("Actual nice value now: %d\n", prio);

    return EXIT_SUCCESS;
}
    
Run examples:
./t_setpriority p 0 5 — Set calling process to nice 5
./t_setpriority p 1234 10 — Set PID 1234 to nice 10 (must be same user)
sudo ./t_setpriority p 1234 -5 — Raise priority (root only)

🎯 Interview Questions

Q1. What is the range of the nice value in Linux, and what does each end mean?
The nice value ranges from -20 (highest scheduling priority, gets the most CPU time) to +19 (lowest scheduling priority, gets the least CPU time). The default is 0. Negative values give a process preferential treatment but traditionally require root/CAP_SYS_NICE privilege.
Q2. Why must you set errno = 0 before calling getpriority()?
Because getpriority() can legitimately return -1 as a valid nice value (when a process has priority level -1). The standard way to detect errors — checking for a -1 return — would give a false alarm. Setting errno = 0 before the call and then checking if (return == -1 && errno != 0) correctly distinguishes a valid -1 result from an error.
Q3. Can an unprivileged process raise its own scheduling priority (lower nice value)?
Traditionally, no — unprivileged processes could only lower their priority (increase nice value). However, since kernel 2.6.12, the RLIMIT_NICE resource limit allows an unprivileged process to raise its priority up to the ceiling defined by 20 − rlim_cur. For example, if RLIMIT_NICE = 25, the process can lower its nice value to as low as -5.
Q4. What does the PRIO_PGRP flag do in getpriority() when multiple processes match?
When multiple processes match (e.g., PRIO_PGRP or PRIO_USER), getpriority() returns the nice value of the process with the highest priority — that is, the lowest numerical nice value among all matching processes.
Q5. Is a process with nice value +19 ever completely starved of CPU time?
No. The nice value is a weighting factor, not an absolute ceiling. Even a process with nice +19 (the lowest priority) is guaranteed to receive some CPU time — it just receives much less than higher-priority processes. This is unlike realtime scheduling (SCHED_FIFO/SCHED_RR) where a lower-priority process can be completely excluded from the CPU as long as a higher-priority realtime process is runnable.
Q6. What is the difference between nice(1) command and renice(8) command?
nice(1) is used to launch a new command with a specified nice value (e.g., nice -n 10 ./myjob). Unprivileged users can lower priority; root can raise it. renice(8) changes the nice value of an already-running process by its PID. The superuser can use renice to adjust any process’s priority.
Q7. Does a child process inherit the parent’s nice value?
Yes. The nice value is inherited by a child process created via fork() and is also preserved across exec(). This means if you run a shell with nice +5 and launch a program from it, the program also starts with nice +5.

Leave a Reply

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