Chapter 8 — Scheduling: who gets the CPU, and for how long
In one sentence
With more runnable processes/threads than CPU cores (almost always true), the kernel's scheduler decides, many times per second, which one runs next and for how long — and the fairness and responsiveness of your entire machine depends on how good that decision is.
Why this matters
"Why does my high-priority job feel slow" and "why did this container get
throttled" both trace back to scheduling decisions — and nice/cgroup CPU
limits (Chapter 43) are literally you giving the scheduler instructions.
How it actually works
THE BASIC PROBLEM: you might have 4 CPU cores and 200 runnable
processes. The scheduler has to pick which handful run RIGHT NOW, and
for how long, before switching to the next batch -- fairly enough that
nothing starves, responsively enough that interactive programs (your
mouse cursor, your SSH session) don't feel laggy, and efficiently
enough that the switching itself (Chapter 9) doesn't eat all your
CPU.
LINUX'S SCHEDULER (CFS -- the Completely Fair Scheduler, the default
since 2007, itself succeeded by EEVDF in newer kernels) works, in
spirit, like this:
- every runnable task accumulates "virtual runtime" while it runs
- the scheduler always picks the task with the LEAST virtual
runtime to run next -- i.e., whoever has had the LEAST CPU time
so far, relatively speaking, goes next.
- this naturally converges toward fairness: no task can hog the CPU
indefinitely, because the more it runs, the further behind
(in scheduling priority) it falls relative to everyone else.
NICE VALUES let you bias this, without breaking fairness entirely:
`nice -n 10 mycommand` lower priority (be nice to others,
range -20 to +19, higher number = lower
priority)
`nice -n -10 mycommand` higher priority (needs privilege)
This doesn't GUARANTEE a task runs sooner -- it changes the WEIGHT the
scheduler uses when deciding, so a "niced-up" process accumulates
virtual runtime slower, and gets picked more often, relatively.
Real-world example
The move FROM Linux's older "O(1) scheduler" TO the Completely Fair
Scheduler (CFS) in 2007 (kernel 2.6.23, engineered by Ingo Molnar) is
a well-documented real case study in why scheduling fairness matters
at scale. The O(1) scheduler's heuristics for distinguishing
"interactive" from "CPU-hungry" tasks were complex, hard to tune, and
produced noticeably unfair or laggy behaviour under certain real
workloads -- users and kernel developers reported cases where
interactive tasks (things you'd expect to feel snappy, like typing in
a terminal) got starved by batch workloads. CFS replaced the
heuristic-heavy approach with the much simpler "always run whoever
has had the least CPU time so far" fairness principle described
above, and became the long-standing Linux default specifically
because it behaved more predictably and fairly across a huge range of
real workloads without needing per-workload tuning.
Try it yourself
# Watch nice values change real scheduling behaviour
nice -n 19 yes > /dev/null & # low priority CPU hog
yes > /dev/null & # normal priority CPU hog
top # compare %CPU for the two `yes` processes; the niced one should
# get noticeably less CPU time on a busy system. Ctrl-C both after.
kill %1 %2
# See the current nice value of every process
ps -eo pid,ni,cmd | head -20
Build it in Go
A container's CPU limit is a CFS quota, not fewer CPUs, so programs that size thread pools by CPU count get throttled. Go 1.25+ reads the cgroup limit and sets GOMAXPROCS to match. Chapter 75 measures the difference: forcing GOMAXPROCS=8 under a 1.5-CPU limit cost 13.7 s of throttling in 2 s.
Common mistakes
- Thinking
niceguarantees priority. It's a WEIGHT/bias for the scheduler's fairness algorithm, not a hard priority override — a heavily niced-down process still gets SOME CPU time, just less. - Assuming more CPU cores means the scheduler matters less. With enough runnable tasks (very common on a busy server or a laptop with many background apps), scheduling decisions matter at any core count.
- Confusing
nice(CPU scheduling priority) withionice(I/O scheduling priority) or cgroup CPU limits (Chapter 43, a hard cap, not just a bias).
Check yourself
- In one sentence, what problem is the scheduler solving?
- Roughly how does Linux's Completely Fair Scheduler decide what runs next?
- Does a negative
nicevalue guarantee a process runs immediately? Why or why not?
Further reading
- Book (free): OSTEP, the scheduling chapters (MLFQ and CFS) — the clearest conceptual walkthrough available.
- Documentation: the Linux kernel's own
Documentation/scheduler/directory (sched-design-CFS.rst) — written by CFS's own author. - Reference:
man 1 nice,man 2 sched_setscheduler.