← Previous Lecture
|
Next Lecture →
Linux Scheduler Classes Explained: How the Kernel Decides What Runs Next
CPU Scheduling Internals – Part 2A | Updated for Linux Kernel 6.12+ / 7.x | Free Linux Kernel Development Course
Scheduler Classes
EEVDF Era
Free Course
Linux scheduler classes are the heart of how the kernel decides which thread gets the CPU next. In this lecture of our free Linux kernel development course, we break down the modular design of Linux scheduler classes in modern kernels – including the EEVDF fair scheduler introduced in kernel 6.6 and the brand-new BPF-based sched_ext class added in kernel 6.12. If you have read older books or blogs, much of what they describe has changed, and this tutorial brings you fully up to date with what runs inside today’s 6.x and 7.x kernels.
This post is part of the EmbeddedPathashala free embedded systems course track, and pairs naturally with our free Linux device drivers course, because drivers constantly interact with scheduling – every time a driver sleeps, wakes a thread, or defers work, the scheduler classes described here decide what happens next.
What You Will Learn
- What Linux scheduler classes are and why the scheduler was made modular
- All six scheduler classes in modern kernels and their strict priority order
- How scheduling policies like
SCHED_FIFO,SCHED_RR,SCHED_DEADLINE,SCHED_OTHER, andSCHED_EXTmap onto scheduler classes - What changed since older kernels: EEVDF replacing CFS, and
sched_extarriving in kernel 6.12 - How to inspect and change a thread’s scheduling policy from the command line
Prerequisites
- Basic C programming and comfort reading kernel-style code
- Understanding of processes and threads in Linux (covered earlier in this free Linux kernel development course)
- A Linux machine (any recent distribution with kernel 6.x or newer) to try the commands
Why Linux Scheduler Classes Exist: The Modular Design
Back in October 2007, kernel release 2.6.23 restructured the scheduler around a simple but powerful idea: instead of one giant algorithm trying to satisfy every kind of workload, the core scheduler became a thin dispatcher sitting on top of several self-contained scheduler classes. Each class implements its own policy logic behind a common interface. This work, led by Ingo Molnar and other core developers, is the foundation the scheduler still stands on today.
The design is essentially object-oriented, even though the kernel is written in C. Each class is described by a struct sched_class containing function pointers – methods such as “enqueue a task”, “dequeue a task”, and “pick the next task to run”. The core scheduler never needs to know how a class chooses its next thread; it simply asks each class in priority order.
One word of caution about the term modular here: it means the scheduler code is cleanly separated into pluggable class implementations compiled into the kernel image. It has nothing to do with loadable kernel modules (LKMs). You cannot insmod a scheduler class. Interestingly though, the newest class, sched_ext, gets remarkably close to that dream by letting you load scheduling policies as BPF programs at runtime – more on that below.
The Six Linux Scheduler Classes in Modern Kernels
Older material (based on kernels around 5.4) lists five classes. Modern kernels – from 6.12 onwards, including the current 6.18 LTS and 7.x stable series – have six scheduler classes when the extensible scheduler is enabled. Here they are, from highest to lowest priority:
| stop_sched_class | Highest priority. Runs the per-CPU migration/N threads for critical kernel work like CPU hotplug and active load balancing. Not selectable by user space. |
| dl_sched_class | Deadline scheduling (SCHED_DEADLINE). Tasks declare runtime, period, and deadline; the kernel guarantees them CPU time using EDF + CBS algorithms. |
| rt_sched_class | POSIX soft real-time (SCHED_FIFO, SCHED_RR) with static priorities 1–99. Common in industrial, audio, and robotics workloads. |
| fair_sched_class | The default class for normal tasks (SCHED_OTHER/SCHED_NORMAL, SCHED_BATCH, SCHED_IDLE). Since kernel 6.6 it runs the EEVDF algorithm, which replaced CFS. |
| ext_sched_class | The extensible class (SCHED_EXT), new in kernel 6.12. Lets you implement a complete scheduling policy as a BPF program loaded at runtime. |
| idle_sched_class | Lowest priority. Runs the per-CPU idle thread (swapper/N) when absolutely nothing else is runnable. Always has something to offer the CPU. |
The corresponding declarations live in the scheduler’s internal header. A simplified view from a current kernel source tree:
/* kernel/sched/sched.h (simplified, modern 6.x/7.x kernels) */
extern const struct sched_class stop_sched_class;
extern const struct sched_class dl_sched_class;
extern const struct sched_class rt_sched_class;
extern const struct sched_class fair_sched_class;
extern const struct sched_class ext_sched_class; /* kernel 6.12+, CONFIG_SCHED_CLASS_EXT */
extern const struct sched_class idle_sched_class;
What Changed Since Kernel 5.4? A Quick Modernization Table
If you learned Linux scheduler classes from older books, the following table summarizes exactly what has moved on. This is one of the most important sections of this free Linux kernel development course lecture, because interviews frequently probe whether you know the current design:
| Aspect | Kernel ~5.4 (older tutorials) | Modern Kernel (6.12+ / 7.x) |
|---|---|---|
| Number of classes | 5 (stop, dl, rt, fair, idle) | 6 – ext_sched_class added between fair and idle |
| Fair-class algorithm | CFS (Completely Fair Scheduler) | EEVDF (Earliest Eligible Virtual Deadline First), since 6.6 |
| Class iteration | Singly linked list via a next pointer inside struct sched_class |
Contiguous array laid out by the linker; simple pointer arithmetic, no next pointer |
| Custom policies | Required patching and rebuilding the kernel | Loadable at runtime as BPF programs via sched_ext |
| Tuning knobs | Mostly under /proc/sys/kernel/sched_* |
Largely relocated to /sys/kernel/debug/sched/ |
Scheduling Policies vs Linux Scheduler Classes
Beginners often confuse two related concepts, so let’s separate them clearly:
- A scheduling policy is a user-visible label attached to a thread. You can query and change it with system calls or utilities. Examples:
SCHED_OTHER,SCHED_FIFO,SCHED_RR,SCHED_BATCH,SCHED_IDLE,SCHED_DEADLINE, andSCHED_EXT. - A scheduler class is the internal kernel implementation that services one or more policies.
Every thread has exactly one policy at any moment (though it can be changed at runtime), and that policy determines exactly one scheduler class. Inside each thread’s task structure (struct task_struct), the policy member records the policy, and a const struct sched_class *sched_class pointer records which class currently owns the thread.
| User-Space Policy | Kernel Scheduler Class | Typical Use Case |
|---|---|---|
SCHED_OTHER / SCHED_NORMAL |
fair_sched_class |
Everything by default: shells, editors, browsers, daemons |
SCHED_BATCH |
fair_sched_class |
CPU-heavy background jobs (compilers, batch analytics) |
SCHED_IDLE |
fair_sched_class |
Extremely low importance work (indexing, cleanup) |
SCHED_FIFO, SCHED_RR |
rt_sched_class |
Soft real-time: audio pipelines, motor control loops |
SCHED_DEADLINE |
dl_sched_class |
Periodic tasks with hard timing budgets |
SCHED_EXT |
ext_sched_class |
Custom BPF schedulers (gaming, datacenter experiments) |
| (kernel internal) | stop_sched_class, idle_sched_class |
Migration threads and per-CPU idle threads |
Note that SCHED_EXT tasks are only truly serviced by the extensible class while a BPF scheduler is actually loaded; otherwise the kernel gracefully falls back to fair-class behavior. This safety net is a deliberate design choice – a buggy BPF scheduler can be ejected by the kernel at any time without hanging the machine.
Hands-On: Inspecting Policies on a Live System
Theory becomes real when you poke at a running kernel. All the commands below are safe to run on your laptop and are the same tools you will use throughout this free embedded systems course track.
View the policy and real-time priority of every thread:
ps -eLo pid,tid,cls,rtprio,comm | head -20
The cls column shows TS for normal (time-sharing) tasks, FF for SCHED_FIFO, RR for round-robin, DLN for deadline tasks, and IDL for idle-policy tasks. On a typical desktop, almost everything is TS, with a handful of FF threads belonging to kernel subsystems.
Query one specific process using chrt:
chrt -p 1
# pid 1's current scheduling policy: SCHED_OTHER
# pid 1's current scheduling priority: 0
Promote a thread to soft real-time (be careful – a runaway FIFO task at high priority can starve your desktop):
sudo chrt -f -p 50 <PID> # SCHED_FIFO, priority 50
sudo chrt -r -p 30 <PID> # SCHED_RR, priority 30
sudo chrt -o -p 0 <PID> # back to SCHED_OTHER
Programmatically, the modern interface is sched_setattr()/sched_getattr(), which supersedes the older sched_setscheduler() because it can also express deadline parameters:
#include <linux/sched.h>
#include <sys/syscall.h>
#include <unistd.h>
struct sched_attr attr = {
.size = sizeof(attr),
.sched_policy = SCHED_FIFO,
.sched_priority = 50,
};
/* glibc has no wrapper; invoke the raw syscall */
if (syscall(SYS_sched_setattr, 0 /* self */, &attr, 0) == -1)
perror("sched_setattr");
Common Mistakes and Troubleshooting
- Assuming CFS still runs your desktop. Since kernel 6.6, the fair class implements EEVDF. Many CFS-era tunables (like
sched_latency_ns) no longer exist. - Confusing modular classes with kernel modules. Scheduler classes are compiled in; only
sched_extpolicies load at runtime, and those are BPF programs, not.kofiles. - Setting
SCHED_FIFOpriority 99 casually. You can starve critical kernel threads and freeze the system. Start low, and preferSCHED_RRwhile experimenting. - Forgetting privileges. Raising a thread into the RT or deadline class requires
CAP_SYS_NICEor appropriateRLIMIT_RTPRIOlimits; otherwisesched_setattr()fails withEPERM. - Looking for tunables in the old place. On modern kernels, check
/sys/kernel/debug/sched/(requires debugfs mounted) instead of/proc/sys/kernel/for most scheduler knobs.
Best Practices
- Keep normal applications in the fair class; reach for RT or deadline policies only when you have measured a genuine latency requirement.
- When you do use
SCHED_FIFO/SCHED_RR, always design a bounded execution path – RT threads must voluntarily sleep or yield. - Use
SCHED_DEADLINEfor periodic control loops in embedded Linux; it gives mathematically-backed guarantees that static priorities cannot. - For experimentation and learning,
sched_extis a superb sandbox: you can watch your own scheduling decisions play out without rebuilding the kernel.
Key Takeaways
- The Linux scheduler is a thin core plus pluggable scheduler classes, a design introduced in kernel 2.6.23 and still evolving.
- Modern kernels have six classes in strict priority order: stop → deadline → real-time → fair → ext → idle.
- Each thread carries one policy and one class at a time, both changeable at runtime.
- EEVDF (kernel 6.6) replaced CFS inside the fair class;
sched_ext(kernel 6.12) made scheduling policies runtime-loadable via BPF.
Frequently Asked Questions (FAQ)
Six on kernels 6.12 and newer with the extensible scheduler enabled: stop, deadline, real-time, fair, ext, and idle – asked in that strict priority order every time the kernel schedules.
No. Kernel 6.6 replaced the CFS logic inside the fair class with EEVDF (Earliest Eligible Virtual Deadline First). The class name fair_sched_class and the default policy SCHED_OTHER are unchanged, but the pick algorithm and tunables are different.
No. Policy and class are exclusive per thread. A thread can migrate between classes at runtime (for example via chrt or sched_setattr()), but it always belongs to exactly one class at any instant.
sched_ext, merged in kernel 6.12, lets developers write a full CPU scheduling policy as a BPF program and load it at runtime. If the BPF scheduler misbehaves, the kernel automatically ejects it and falls back to the fair class, making experimentation safe.
Both map to rt_sched_class, with static priorities 1–99. FIFO threads run until they block or yield; RR threads additionally rotate with siblings at the same priority using a timeslice.
The stop class exists to run per-CPU migration threads, which matter on multiprocessor systems. Since practically all modern kernels are built for SMP, you will see migration/N threads on any current machine.
Right here – EmbeddedPathashala offers a completely free Linux kernel development course, a free Linux device drivers course, and a free embedded systems course covering scheduling, memory management, drivers, and more.
Conclusion
Linux scheduler classes turn a potentially monstrous scheduling problem into a clean, layered design: a tiny core that asks a fixed priority ladder of classes “do you have work?”, and specialized classes that each solve one job well. Understanding this structure is essential groundwork for kernel developers, embedded engineers, and anyone preparing for Linux internals interviews. In Part 2B of this free Linux kernel development course, we go one level deeper: how pick_next_task() actually walks the classes, and how per-CPU runqueues are organized in modern kernels.
Course Keywords
Free Linux Kernel Development Course
Free Linux Device Drivers Course
Free Embedded Systems Course
EEVDF
sched_ext
SCHED_DEADLINE
Kernel Internals
Continue Your Free Linux Kernel Development Course
Next up: how the kernel actually picks the next task, and the truth about runqueues.

2 Comments