Linux Scheduling Policy and Priority: chrt, sched_setattr and SCHED_DEADLINE
Linux Kernel Programming Course | CPU Scheduler Series – Part 2 | Lecture 3
In this lecture of our free Linux kernel development course we take direct control of scheduling urgency: querying and setting a thread’s Linux scheduling policy and priority. You already know from Part 1 that Linux implements the POSIX policies (SCHED_OTHER, SCHED_FIFO, SCHED_RR) plus Linux-specific ones (SCHED_BATCH, SCHED_IDLE, SCHED_DEADLINE). Now we make threads actually use them — from the shell with chrt and from C code with the modern sched_setattr() system call. We also cover what changed in recent kernels: since kernel 6.6, the fair class is implemented by EEVDF (Earliest Eligible Virtual Deadline First), the successor to CFS.
What You Will Learn
- The scheduling class hierarchy on modern kernels and where each policy lives
- Querying and changing policy/priority with
chrtand reading it from procfs - Setting Linux scheduling policy and priority in C with
sched_setscheduler()andsched_setattr() - Using SCHED_DEADLINE for guaranteed CPU time
- Nice values and how EEVDF converts them into virtual deadlines
- Safety: RT throttling, privileges, and how not to freeze your machine
Prerequisites
Lectures 1–2 of this series, working C knowledge, and a Linux system where you have root. As always in this free linux kernel programming course, practice on a VM or development board — a runaway SCHED_FIFO task on your main desktop is a lesson you only need once.
The Policy Landscape on Modern Kernels
| Class | Policies | Priority Range | Typical Use |
|---|---|---|---|
| Stop | (kernel internal) | — | CPU hotplug, migration |
| Deadline | SCHED_DEADLINE | runtime/deadline/period | Periodic real-time work |
| Real-Time | SCHED_FIFO, SCHED_RR | 1 – 99 | Latency-critical threads |
| Fair (EEVDF) | SCHED_OTHER, SCHED_BATCH | nice −20 to +19 | Everything normal |
| Idle | SCHED_IDLE | — | Run only when nothing else wants CPU |
The scheduler walks the classes top-down: if a runnable deadline task exists it always beats FIFO/RR; any runnable RT task beats every fair task; and fair tasks beat SCHED_IDLE. Within the fair class, EEVDF picks the task with the earliest eligible virtual deadline, which improves latency for interactive tasks compared with the old CFS vruntime-only approach — while keeping nice-value semantics you already know.
Querying and Setting Policy from the Shell: chrt
# Query a running process
chrt -p 1234
# pid 1234's current scheduling policy: SCHED_OTHER
# pid 1234's current scheduling priority: 0
# Show valid priority ranges for each policy on this kernel
chrt -m
# Launch a program as SCHED_FIFO priority 50
sudo chrt -f 50 ./control_loop
# Change a running process to SCHED_RR priority 10
sudo chrt -r -p 10 1234
# Back to normal (SCHED_OTHER)
sudo chrt -o -p 0 1234
For nice values within the fair class, the classic pair still applies:
nice -n 10 ./batch_job # start with nice +10 (lower weight)
sudo renice -n -5 -p 1234 # raise a running task's weight
You can also read scheduling info straight from procfs — handy on stripped-down embedded images:
grep -E 'policy|prio' /proc/1234/sched | head -4
Programmatic Control in C
The classic API: sched_setscheduler()
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
struct sched_param sp = { .sched_priority = 50 };
/* pid 0 = calling thread; requires CAP_SYS_NICE */
if (sched_setscheduler(0, SCHED_FIFO, &sp) == -1) {
perror("sched_setscheduler");
exit(EXIT_FAILURE);
}
printf("Now running as SCHED_FIFO prio %d\n", sp.sched_priority);
/* ... latency-critical work here ... */
return 0;
}
The modern API: sched_setattr() and SCHED_DEADLINE
sched_setattr() is the superset interface: it handles every policy including SCHED_DEADLINE, which the older call cannot express. glibc does not wrap it, so we invoke it via syscall():
#define _GNU_SOURCE
#include <linux/sched.h>
#include <linux/sched/types.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int set_deadline(pid_t pid, __u64 runtime_ns,
__u64 deadline_ns, __u64 period_ns)
{
struct sched_attr attr;
memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.sched_policy = SCHED_DEADLINE;
attr.sched_runtime = runtime_ns;
attr.sched_deadline = deadline_ns;
attr.sched_period = period_ns;
return syscall(SYS_sched_setattr, pid, &attr, 0);
}
int main(void)
{
/* Guarantee: 2 ms of CPU every 10 ms, finished within 10 ms */
if (set_deadline(0, 2000000, 10000000, 10000000) == -1) {
perror("sched_setattr(SCHED_DEADLINE)");
exit(EXIT_FAILURE);
}
for (int cycle = 0; cycle < 100; cycle++) {
/* ... do at most ~2 ms of periodic work ... */
sched_yield(); /* done for this period */
}
return 0;
}
The kernel performs admission control: if the sum of all deadline reservations would exceed available CPU capacity, the call fails with EBUSY instead of silently overcommitting. This makes SCHED_DEADLINE the only policy with a mathematical guarantee, and it is increasingly the right choice for periodic embedded workloads. Note that a SCHED_DEADLINE task cannot call fork(), and on mainline kernels it requires root privileges.
Choosing the Right Policy
| Workload | Recommended Policy | Why |
|---|---|---|
| Desktop/app threads | SCHED_OTHER | EEVDF balances fairness and latency automatically |
| Long compile/analytics job | SCHED_BATCH or nice +10 | Signals “throughput over latency” |
| Event-driven RT thread (IRQ handler thread, audio) | SCHED_FIFO 50–80 | Runs until it blocks; minimal wakeup latency |
| Several equal-priority RT threads | SCHED_RR | Adds time-slicing among equals |
| Strictly periodic control loop | SCHED_DEADLINE | Admission-controlled CPU guarantee |
| Background indexer | SCHED_IDLE | Uses only leftover CPU |
Safety: RT Throttling and Not Freezing Your System
A spinning SCHED_FIFO task can starve everything below it — including your shell. As a safety net the kernel throttles the RT classes: by default they may consume 950 ms of every 1000 ms, leaving 5% for the fair class:
cat /proc/sys/kernel/sched_rt_period_us # 1000000
cat /proc/sys/kernel/sched_rt_runtime_us # 950000
Disable it (-1) only on dedicated, well-tested real-time systems. During development, always keep an SSH session at a higher RT priority than the task you are testing, so you can recover.
Common Mistakes and Troubleshooting
- EPERM: setting RT/deadline policies needs root or CAP_SYS_NICE (or an RLIMIT_RTPRIO grant via /etc/security/limits.conf for a user).
- Priority 99 everywhere: priority inflation makes priorities meaningless and competes with critical kernel threads. Design a priority map; leave headroom.
- Busy-waiting in SCHED_FIFO: combine with RT throttling disabled and you freeze the core. Always block (poll, clock_nanosleep) between work items.
- Expecting nice to matter against RT tasks: nice only ranks tasks within the fair class; any RT task beats all of them.
- Priority inversion: when an RT task blocks on a lock held by a normal task, use priority-inheritance mutexes (PTHREAD_PRIO_INHERIT).
Best Practices
- Start with SCHED_OTHER; escalate to RT policies only when tracing (Lecture 1) proves a latency problem.
- Prefer SCHED_DEADLINE over SCHED_FIFO for periodic work — admission control catches overcommitment at design time.
- Pair policy with placement: an RT policy plus a pinned, isolated CPU (Lecture 2) is the standard low-latency recipe.
- Verify with
chrt -pand tracing after deployment; init systems and container runtimes sometimes reset scheduling attributes.
Key Takeaways
- Scheduling classes have strict precedence: deadline > RT (FIFO/RR) > fair (EEVDF) > idle.
chrtis the shell tool;sched_setscheduler()covers classic policies;sched_setattr()covers everything including SCHED_DEADLINE.- EEVDF (kernel 6.6+) replaced CFS but keeps the same user-visible knobs — nice values and policies work as before.
- RT throttling exists to save you; respect it during development.
Conclusion
You can now express exactly how urgent every thread is — from background SCHED_IDLE indexing to mathematically guaranteed SCHED_DEADLINE reservations. Combined with affinity and isolation from the previous lecture, you hold the two main levers of the Linux scheduler. This lecture is one of the most interview-relevant topics in our free Linux kernel programming course, and it appears constantly in embedded roles — which is why our free linux device drivers course and free embedded systems course learners are encouraged to practice every command here. Next, we limit how much CPU a group of tasks may consume, using cgroup v2 bandwidth control.
Frequently Asked Questions (FAQ)
1. What replaced CFS in the Linux kernel?
EEVDF (Earliest Eligible Virtual Deadline First) became the fair-class scheduler in kernel 6.6. Policies, nice values, and tools are unchanged from a user’s perspective.
2. What is the difference between SCHED_FIFO and SCHED_RR?
Both are fixed-priority RT policies. FIFO runs a task until it blocks or is preempted by higher priority; RR additionally time-slices among tasks of the same priority.
3. Why does sched_setattr() need syscall() directly?
glibc has not wrapped it. Calling via syscall(SYS_sched_setattr, …) with a properly sized struct sched_attr is the standard, stable approach.
4. Can a normal user set real-time priorities?
Only if granted: either CAP_SYS_NICE or an rtprio limit configured in /etc/security/limits.conf (used by audio setups, for example).
5. Does a higher nice value mean higher priority?
No — it is the opposite. nice +19 is the weakest, nice −20 the strongest within the fair class.
6. When should I pick SCHED_DEADLINE over SCHED_FIFO?
When your work is periodic with a known worst-case runtime. Deadline scheduling gives a per-task guarantee and rejects infeasible configurations up front.
7. Do these policies behave differently on PREEMPT_RT kernels?
The policies and APIs are identical; PREEMPT_RT (covered in Lecture 5) reduces the kernel-side latency between an RT task becoming runnable and actually running.
Continue Your Free Linux Kernel Development Course
Next up: CPU bandwidth control with cgroups.

2 Comments