Linux-specific scheduling policies for background and idle-priority work

 

SCHED_BATCH and SCHED_IDLE Policies
Chapter 35.2.3 — Linux-specific scheduling policies for background and idle-priority work
← Chapter 35 Index  /  SCHED_BATCH and SCHED_IDLE

Linux-Specific Non-Realtime Policies

The Linux 2.6 kernel series introduced two additional scheduling policies that do not exist in POSIX: SCHED_BATCH and SCHED_IDLE. These are set using the same POSIX realtime scheduling API (sched_setscheduler()), but they are not realtime policies. They are extensions of SCHED_OTHER for special use cases.

📌 Important: Both SCHED_BATCH and SCHED_IDLE are Linux-specific. They are not part of the POSIX standard and will not compile or work on other UNIX systems without modification. Use #ifdef SCHED_BATCH guards when writing portable code.

SCHED_BATCH (Added in Linux 2.6.16)

SCHED_BATCH is designed for batch-style or CPU-intensive non-interactive jobs — programs that run for a long time, consume lots of CPU, and do not need to respond to user input or events quickly.

How It Differs from SCHED_OTHER

SCHED_BATCH is similar to SCHED_OTHER but with one behavioral difference: processes that frequently wake up are scheduled less aggressively. The scheduler assumes that a SCHED_BATCH process that wakes up is not trying to be interactive — it just needs to crunch numbers. This prevents SCHED_BATCH processes from “gaming” the scheduler by doing many short sleeps and wakes to appear interactive.

SCHED_OTHER
  • Normal interactive processes
  • Frequent wake-ups treated as interactive signals
  • Scheduler gives waking process a boost
  • Good for shells, editors, GUI apps
SCHED_BATCH
  • CPU-bound non-interactive jobs
  • Frequent wake-ups do NOT get boost
  • Scheduler assumes CPU-hungry behavior
  • Good for compilers, video encoders, simulations

When to Use SCHED_BATCH

Use SCHED_BATCH when you have a job that:

  • Will run for a long time (minutes to hours)
  • Does not interact with the user
  • Should not interfere with interactive applications
  • Examples: make -j8 (parallel build), FFmpeg video encoding, scientific simulations, backups
📌 Note: For SCHED_BATCH, the sched_priority field must be set to 0 (same as SCHED_OTHER). The nice value still applies and can be used to further adjust relative priority within SCHED_BATCH.

SCHED_IDLE (Added in Linux 2.6.23)

SCHED_IDLE is the lowest-possible scheduling policy in Linux. A process running under SCHED_IDLE will only receive CPU time when no other process on the system wants to run. It is designed for truly background, opportunistic work that should never get in the way of anything else.

Key Properties

Priority
Equivalent to a nice value lower than +19 — even lower than the absolute minimum allowed for SCHED_OTHER. The nice value of a SCHED_IDLE process has no meaning and is ignored.
CPU Share
Gets a significant proportion of the CPU only when no other process needs it. If any SCHED_OTHER or SCHED_BATCH process is runnable, the SCHED_IDLE process will not run.
sched_priority
Must be set to 0 when calling sched_setscheduler() with SCHED_IDLE.

When to Use SCHED_IDLE

Use SCHED_IDLE for processes that should only run during genuine idle time:

  • Background indexers (like file system indexing services)
  • Screen savers doing computation
  • Low-priority data analysis running “whenever possible”
  • Any task where latency does not matter at all

Complete Scheduling Priority Hierarchy

🔴 SCHED_FIFO / SCHED_RR (Priority 99) — Highest
🟠 SCHED_FIFO / SCHED_RR (Priority 50)
🟡 SCHED_FIFO / SCHED_RR (Priority 1) — Lowest RT
↓ Preempts everything below
🟢 SCHED_OTHER (nice -20 … nice 0 … nice +19)
🔵 SCHED_BATCH (similar to SCHED_OTHER, less wakeup bias)
🟣 SCHED_IDLE — Lowest possible (below nice +19)

All Linux Scheduling Policies: Summary Table

Policy POSIX? Realtime? sched_priority Kernel Added Best For
SCHED_OTHER 0 Always Normal processes
SCHED_FIFO 1–99 Always Critical RT tasks
SCHED_RR 1–99 Always Shared RT workloads
SCHED_BATCH 0 2.6.16 CPU-bound batch jobs
SCHED_IDLE 0 2.6.23 True background work

💻 Code Example 1: Set Process to SCHED_BATCH


/* set_batch.c
 * Switches the calling process to SCHED_BATCH scheduling.
 * Suitable for CPU-intensive background jobs.
 *
 * Compile: gcc set_batch.c -o set_batch
 * Run:     ./set_batch
 *
 * Note: SCHED_BATCH can be set without root privileges
 *       (it does not increase priority above SCHED_OTHER).
 */
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <unistd.h>

#ifndef SCHED_BATCH
#define SCHED_BATCH 3   /* Linux-specific; define fallback */
#endif

int main(void)
{
    struct sched_param sp;
    int policy;

    /* SCHED_BATCH requires sched_priority = 0 */
    sp.sched_priority = 0;

    printf("Setting scheduling policy to SCHED_BATCH...\n");

    if (sched_setscheduler(0, SCHED_BATCH, &sp) == -1) {
        perror("sched_setscheduler(SCHED_BATCH)");
        exit(EXIT_FAILURE);
    }

    /* Verify */
    policy = sched_getscheduler(0);
    if (policy == -1) {
        perror("sched_getscheduler");
        exit(EXIT_FAILURE);
    }

    printf("Current policy: %s\n",
           (policy == SCHED_BATCH) ? "SCHED_BATCH" : "OTHER");

    /*
     * Now run the CPU-intensive batch work here.
     * This process will be de-prioritized compared to SCHED_OTHER
     * processes that need to run interactively.
     */
    printf("Running batch work (simulated)...\n");
    volatile long long x = 0;
    for (long long i = 0; i < 100000000LL; i++)
        x += i;   /* CPU-bound busy loop */
    printf("Batch work done. Result: %lld\n", x);

    return EXIT_SUCCESS;
}
    

💻 Code Example 2: Set Process to SCHED_IDLE


/* set_idle.c
 * Switches the calling process to SCHED_IDLE — the lowest
 * possible scheduling policy. The process only runs when
 * no other process on the system wants CPU time.
 *
 * Compile: gcc set_idle.c -o set_idle
 * Run:     ./set_idle
 *
 * Note: SCHED_IDLE can be set without root on most kernels.
 *       Once set, the process CANNOT change its own policy
 *       regardless of RLIMIT_RTPRIO.
 */
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <unistd.h>

#ifndef SCHED_IDLE
#define SCHED_IDLE 5   /* Linux-specific fallback */
#endif

const char *policy_to_str(int p) {
    if (p == SCHED_OTHER) return "SCHED_OTHER";
    if (p == SCHED_BATCH) return "SCHED_BATCH";
    if (p == SCHED_IDLE)  return "SCHED_IDLE";
    if (p == SCHED_FIFO)  return "SCHED_FIFO";
    if (p == SCHED_RR)    return "SCHED_RR";
    return "UNKNOWN";
}

int main(void)
{
    struct sched_param sp;
    int before, after;

    /* Show current policy */
    before = sched_getscheduler(0);
    printf("Before: %s\n", policy_to_str(before));

    /* SCHED_IDLE requires sched_priority = 0 */
    sp.sched_priority = 0;

    if (sched_setscheduler(0, SCHED_IDLE, &sp) == -1) {
        perror("sched_setscheduler(SCHED_IDLE)");
        exit(EXIT_FAILURE);
    }

    after = sched_getscheduler(0);
    printf("After:  %s\n", policy_to_str(after));

    /*
     * WARNING: Once we are SCHED_IDLE, we CANNOT change our
     * own policy back (unless we are root or have RLIMIT_RTPRIO).
     * The SCHED_IDLE policy ignores RLIMIT_RTPRIO for self-changes.
     *
     * Attempting to change back to SCHED_OTHER will fail:
     */
    sp.sched_priority = 0;
    if (sched_setscheduler(0, SCHED_OTHER, &sp) == -1) {
        perror("Can't change from SCHED_IDLE (as expected)");
    } else {
        printf("Changed back to SCHED_OTHER (you have root)\n");
    }

    printf("Running idle-priority background work...\n");
    /* This process will only use CPU when system is idle */
    for (int i = 0; i < 5; i++) {
        sleep(1);
        printf("  Idle work tick %d\n", i+1);
    }

    return EXIT_SUCCESS;
}
    
Key behavior: Once a process is set to SCHED_IDLE, it cannot change its own scheduling policy back (regardless of RLIMIT_RTPRIO). Only root (CAP_SYS_NICE) can change it. This is a special restriction unique to SCHED_IDLE.

🎯 Interview Questions

Q1. What is the purpose of SCHED_BATCH, and how does it differ from SCHED_OTHER?
SCHED_BATCH is designed for CPU-bound, non-interactive batch jobs like video encoders or compilers. It behaves like SCHED_OTHER except that processes that frequently wake up are scheduled less often — the scheduler doesn’t give them the interactive wakeup boost that SCHED_OTHER processes receive. This makes batch jobs less likely to interfere with interactive work.
Q2. What is SCHED_IDLE and when would you use it?
SCHED_IDLE provides a scheduling priority lower than nice +19. A process under SCHED_IDLE only gets CPU time when absolutely no other process (SCHED_OTHER, SCHED_BATCH, or realtime) wants to run. It’s useful for truly opportunistic background work like background indexing, screensavers doing computation, or any task where latency is irrelevant and you want zero interference with normal work.
Q3. Are SCHED_BATCH and SCHED_IDLE part of the POSIX standard?
No. Both are Linux-specific extensions. POSIX defines only SCHED_OTHER, SCHED_FIFO, and SCHED_RR. When writing portable code that may run on non-Linux systems, you must wrap usage of SCHED_BATCH and SCHED_IDLE in #ifdef SCHED_BATCH and #ifdef SCHED_IDLE guards.
Q4. What sched_priority value must you use with SCHED_BATCH and SCHED_IDLE?
Both policies require sched_priority = 0. Any other value will cause sched_setscheduler() to fail. The nice value still has meaning for SCHED_BATCH but is ignored for SCHED_IDLE.
Q5. Can a SCHED_IDLE process change its own scheduling policy back to SCHED_OTHER?
No. SCHED_IDLE has a special restriction: a process operating under SCHED_IDLE cannot change its own scheduling policy, regardless of the RLIMIT_RTPRIO resource limit. Only a privileged process (CAP_SYS_NICE / root) can change the policy of a SCHED_IDLE process. This is a deliberate design choice to prevent SCHED_IDLE processes from escalating their own priority.

Leave a Reply

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