SchedulingIntermediate

Why a scheduler exists

“Why does the OS need a scheduler at all, and what is it optimising for? What could go wrong with the simplest scheme you can think of?”

What this tests

  • Scheduling as a response to a constraint (more runnable tasks than cores)
  • The conflicting goals: throughput, latency, fairness
  • Preemption and why cooperative scheduling fails
  • What blocked tasks cost and what the application can influence

Answers by level

Read the beginner answer first and notice what is missing.

Start with the constraint: a core executes one instruction stream, and at any moment there may be many tasks that *could* run. Something has to pick, and it has to pick again a few milliseconds later when the situation changes. That something is the scheduler, and it is a policy with conflicting goals — throughput (finish the most work), latency (a keypress or an incoming packet is handled promptly), fairness (no task starves), and increasingly power and cache locality (The Scheduling Problem).

The simplest scheme — run a task until it finishes — collapses on the first infinite loop; nothing else ever runs again. So the scheduler needs preemption: a timer interrupt ends the slice whether the task likes it or not. Cooperative systems (Windows 3.1, classic Mac OS — and every event loop) depend on tasks yielding; one misbehaving task freezes everything, which is exactly what a blocked Node.js event loop looks like (The Event Loop).

The slice length is the central trade-off. Long slices mean fewer switches and better throughput but poor responsiveness; short slices mean the opposite. Schedulers therefore treat tasks differently by behaviour: an I/O-bound task that uses a sliver of its slice and blocks should be run promptly when it wakes; a CPU-bound task can wait. Priorities (nice on Unix, priority classes on Windows) let users and the kernel bias that.

A crucial point that beginners miss: blocked tasks are not scheduled at all. A process sleeping in read() costs the scheduler nothing; it is in a wait queue on the socket, not in the ready queue. The load a scheduler feels is the number of *runnable* tasks, which is what Linux’s load average approximates (with the twist that Linux also counts tasks in uninterruptible I/O wait) (Process States).

Green flags · Red flags

Strong green flag · Connects the event loop to cooperative scheduling and cgroup quotas to throttling latency.
Green flags
  • Frames the scheduler as a policy balancing throughput, latency and fairness
  • Explains why preemption is required and what cooperative scheduling breaks
  • Says blocked tasks are not in the ready queue
  • Distinguishes I/O-bound and CPU-bound behaviour
  • Knows nice/priority is a bias, not a guarantee
Red flags
  • Only knows round-robin
  • Believes sleeping processes consume CPU or scheduler time
  • Thinks a higher priority makes code run faster

Follow-up questions

F1
Why does a chat app feel responsive even while a compile runs at 100%?
F2
Load average is 12 on a 4-core box but CPU is 20% busy. What?
F3
What does a CPU limit of 0.5 do to a bursty service in Kubernetes?

Scenario

A latency-sensitive gateway shares a host with a nightly batch job. When the batch runs, p99 latency triples although CPU never reaches 100%. What scheduler mechanisms explain it and what would you change?

Learn this topic