Why Standard Scheduling Is Not Enough
The round-robin time-sharing scheduler (SCHED_OTHER) works great for desktop and server workloads. But some applications have much stricter requirements. Consider a vehicle navigation system, an industrial robot controller, or a cardiac monitor. These systems must respond to events within a guaranteed maximum time. If the scheduler decides to run a background process for 50ms when the navigation system urgently needs the CPU, the result could be catastrophic.
Such applications are called real-time applications, and they have two key requirements that the standard scheduler cannot satisfy:
A high-priority process must be able to seize the CPU from any currently running process within a known, short time. Standard scheduling offers no such guarantee.
A high-priority realtime process must be able to hold the CPU exclusively until it finishes its critical task, without being preempted by lower-priority processes.
Soft Realtime vs Hard Realtime
The POSIX realtime API provides what is called soft realtime. This is an important distinction to understand.
| Feature | Soft Realtime (POSIX API) | Hard Realtime (RTOS) |
|---|---|---|
| Guaranteed max response time | ✗ No | ✓ Yes |
| Priority-based preemption | ✓ Yes | ✓ Yes |
| Exclusion of lower-priority processes | ✓ Yes | ✓ Yes |
| Works on standard Linux kernel | ✓ Yes | ✗ Needs PREEMPT_RT patch |
| Overhead on normal processes | ✓ Low | ✗ Higher |
The POSIX Realtime Scheduling Policies
The POSIX standard defines a realtime scheduling API (originally from POSIX.1b) that adds two realtime policies to the existing standard policy:
• Has a time slice
• Processes at same priority share CPU
• Priority 1 (low) to 99 (high)
• No time slice
• Runs until blocks or yields
• Priority 1 (low) to 99 (high)
Realtime Priority Levels: 1 to 99
Linux provides 99 realtime priority levels, numbered from 1 (lowest) to 99 (highest). These levels are completely separate from the nice value system.
The SCHED_RR and SCHED_FIFO policies share the same priority range (1–99). A SCHED_RR process at priority 50 and a SCHED_FIFO process at priority 50 are considered equal in terms of priority — which one runs first depends on their position in the queue for that priority level.
sched_get_priority_min(policy) and sched_get_priority_max(policy) to get the valid range at runtime.How the Queue Model Works
Each priority level maintains its own queue of runnable processes. The scheduler always picks from the front of the highest non-empty queue.
| Priority 99 |
P-A (FIFO)
→ next to run
|
← CPU goes here first |
| Priority 50 |
P-B (RR)
P-C (FIFO)
|
Waits while P99 runs |
| Priority 1 |
P-D (RR)
|
Runs last |
| SCHED_OTHER |
P-E
P-F
P-G
|
Only runs when no RT process is runnable |
⚠️ Multiprocessor Systems: A Critical Nuance
On a multiprocessor or hyperthreaded system, each CPU maintains its own separate run queue. Processes are prioritized per CPU queue, not system-wide. This can lead to surprising behavior:
To avoid this in time-critical applications, use the CPU affinity API (covered in File 7) to isolate critical realtime processes to dedicated CPUs.
💻 Code Example 1: Query Priority Ranges
Use sched_get_priority_min() and sched_get_priority_max() to discover the valid realtime priority range on the current system — never hard-code these values.
/* query_rt_range.c
* Shows the valid realtime priority ranges for SCHED_FIFO and SCHED_RR.
* Compile: gcc query_rt_range.c -o query_rt_range
* Run: ./query_rt_range
*/
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
void show_range(int policy, const char *name)
{
int min_prio, max_prio;
min_prio = sched_get_priority_min(policy);
if (min_prio == -1) {
perror("sched_get_priority_min");
return;
}
max_prio = sched_get_priority_max(policy);
if (max_prio == -1) {
perror("sched_get_priority_max");
return;
}
printf("Policy %-12s : min = %3d, max = %3d\n",
name, min_prio, max_prio);
}
int main(void)
{
printf("=== Realtime Scheduling Priority Ranges ===\n\n");
show_range(SCHED_FIFO, "SCHED_FIFO");
show_range(SCHED_RR, "SCHED_RR");
show_range(SCHED_OTHER, "SCHED_OTHER");
/* Portable way to specify a mid-range realtime priority */
int min_rt = sched_get_priority_min(SCHED_FIFO);
int max_rt = sched_get_priority_max(SCHED_FIFO);
int mid_rt = min_rt + (max_rt - min_rt) / 2;
printf("\nPortable mid-range RT priority: %d\n", mid_rt);
printf("One step above minimum: %d\n", min_rt + 1);
printf("One step below maximum: %d\n", max_rt - 1);
return EXIT_SUCCESS;
}
Policy SCHED_FIFO : min = 1, max = 99
Policy SCHED_RR : min = 1, max = 99
Policy SCHED_OTHER : min = 0, max = 0
Portable mid-range RT priority: 50
💻 Code Example 2: Check Current Process’s Scheduling Policy
Read your own process’s scheduling policy and priority, and detect whether you are running under a realtime policy.
/* check_my_policy.c
* Reads and prints the scheduling policy and priority of the calling process.
* Compile: gcc check_my_policy.c -o check_my_policy
* Run: ./check_my_policy
* sudo ./check_my_policy (if changed by parent or env)
*/
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
const char *policy_name(int policy)
{
switch (policy) {
case SCHED_OTHER: return "SCHED_OTHER (standard round-robin)";
case SCHED_FIFO: return "SCHED_FIFO (realtime FIFO)";
case SCHED_RR: return "SCHED_RR (realtime round-robin)";
#ifdef SCHED_BATCH
case SCHED_BATCH: return "SCHED_BATCH (Linux batch)";
#endif
#ifdef SCHED_IDLE
case SCHED_IDLE: return "SCHED_IDLE (Linux idle)";
#endif
default: return "UNKNOWN";
}
}
int main(void)
{
int policy;
struct sched_param sp;
/* Pass 0 to operate on the calling process */
policy = sched_getscheduler(0);
if (policy == -1) {
perror("sched_getscheduler");
exit(EXIT_FAILURE);
}
if (sched_getparam(0, &sp) == -1) {
perror("sched_getparam");
exit(EXIT_FAILURE);
}
printf("=== My Scheduling Information ===\n");
printf("Policy : %s\n", policy_name(policy));
printf("Priority : %d\n", sp.sched_priority);
/* Check if we are under a realtime policy */
if (policy == SCHED_FIFO || policy == SCHED_RR) {
printf("Status : REALTIME process\n");
printf("Priority range: %d to %d\n",
sched_get_priority_min(policy),
sched_get_priority_max(policy));
} else {
printf("Status : Normal (non-realtime) process\n");
printf("Note : Realtime priority has no meaning for this policy\n");
}
return EXIT_SUCCESS;
}
Policy : SCHED_OTHER (standard round-robin)
Priority : 0
Status : Normal (non-realtime) process
🎯 Interview Questions
Soft realtime provides best-effort priority-based preemption. A high-priority process will usually get the CPU quickly, but there is no absolute guarantee. The POSIX realtime API on standard Linux provides soft realtime.
sched_get_priority_min() and sched_get_priority_max() and express priorities relative to these values.