01. Introduction to CPU Scheduling

In any multiprogrammed system, multiple processes and execution threads reside simultaneously in main memory. While modern hardware platforms provide multi-core processors, the number of active, runnable tasks almost always exceeds the number of physical execution units.

The operating system must decide: which process runs next, on which CPU core, for how long, and when it must be forced to yield? This critical allocation policy is governed by the CPU scheduler, while the low-level mechanism that enacts the switch is executed by the dispatcher.

1. Process Behavior: The CPU-I/O Burst Cycle

Nearly all processes alternate between two distinct execution phases in an unending cycle:

The Empirical Burst Distribution

Decades of empirical measurements across production systems demonstrate that CPU burst durations follow a heavy-tailed, hyperexponential frequency distribution:

Figure 1.1: Empirical Frequency Distribution of CPU Burst Durations
High frequency of sub-10ms bursts versus rare, long-running compute bursts.
Frequency (%) CPU Burst Duration (Milliseconds) → 0 50% 100% 2ms 8ms 20ms 50ms 100ms+ I/O-Bound Cluster (>80% of Bursts) Interactive, text editors, GUI events, short-lived system calls. CPU-Bound Tail (<20% of Bursts) Simulations, matrix multiplication, long uninterrupted compilation.

Compute-Bound vs. I/O-Bound Workloads

An operating system exploits this asymmetry to achieve balanced system utilization:

2. The Scheduler vs. The Dispatcher

In operating system architecture, execution scheduling is cleanly partitioned according to the fundamental computer science principle of separating policy from mechanism:

Component Domain & Layer Core Responsibilities Execution Invariants
CPU Scheduler Policy Layer
(Abstract algorithmic logic)
• Evaluates Ready queue state
• Computes dynamic priority boosts
• Decrements remaining time quantums
• Selects the next task_struct / KTHREAD
Runs inside kernel mode; may execute concurrently on multi-core systems with per-CPU runqueue spinlocks.
The Dispatcher Mechanism Layer
(Silicon-level control)
• Saves outgoing registers & stack pointer
• Updates MMU address mapping (CR3)
• Configures hardware TLS registers
• Drops privilege: Ring 0 → Ring 3
Must execute with local interrupts disabled (CLI); must never block or page-fault during a switch.

1. Low-Level Execution Sequence of a Dispatch Cycle

Whenever the scheduler decides to replace Outgoing Process A with Incoming Process B, the dispatcher executes an exacting, deterministic sequence of machine-level operations:

  1. Interrupt Trapping & Mode Transition (Ring 3 → Ring 0): The CPU transitions from unprivileged User Mode to supervisor Kernel Mode via a hardware interrupt (e.g., local APIC timer tick) or an explicit software trap (e.g., synchronous syscall instruction). The CPU hardware automatically pushes the User Stack Pointer (RSP), Program Counter (RIP), and Flags register (RFLAGS) onto the process's private kernel stack.
  2. Hardware Context Preservation: The kernel pushes all remaining general-purpose registers (RAX, RBX, RCX, RDX, RSI, RDI, RBP, R8–R15) onto the kernel stack, assembling a complete hardware trap frame (struct pt_regs in Linux, KTRAP_FRAME in Windows). The resulting stack pointer is written into Outgoing Task A's Thread Control Block.
  3. Task State Accounting & Scheduler Invocation: The kernel dispatcher records the consumed CPU cycles in Task A's execution counter. If Task A blocked on I/O, its state is set to TASK_INTERRUPTIBLE and it is placed into a wait queue. If its quantum expired, its state remains TASK_RUNNING and it is enqueued at the tail of the Ready list. The scheduler then selects Incoming Task B.
  4. Virtual Memory Context Switch (MMU CR3 Reloading): If Task B belongs to a different process than Task A, the dispatcher reloads the CPU's CR3 control register (or TTBR0 on ARM) with the physical base address of Task B's page table root:
    /* Low-level x86-64 assembly in kernel dispatcher: */
    movq  %rax, %cr3   /* Loads new page table directory; invalidates non-global TLB entries */
    Note on Thread Switching: If Task A and Task B are two threads within the same process, they share the identical memory descriptor (task->mm). The dispatcher detects this identity and completely bypasses reloading CR3, preserving warm Translation Lookaside Buffer entries.
  5. Hardware TLS Segment Register Reconfiguration: The dispatcher updates the architecture-specific segment register base address pointing to the thread's user-mode Thread Control Block (writing to Model-Specific Register IA32_FS_BASE via WRMSR on Linux, or IA32_GS_BASE on Windows).
  6. Kernel Stack Pointer Swap (TSS Update): The CPU's stack pointer register (RSP) is updated to point to Incoming Task B's saved kernel stack. Simultaneously, the kernel updates the privileged Task State Segment (TSS) so that future hardware interrupts while Task B runs in user mode will automatically vector to Task B's kernel stack rather than Task A's.
  7. Hardware Context Restoration: The dispatcher executes a sequence of POPQ assembly instructions, loading Task B's saved general-purpose registers from its kernel stack back into the silicon register files.
  8. Privilege Drop and Instruction Jump (Ring 0 → Ring 3): The dispatcher executes the IRETQ (interrupt return) or SYSRETQ instruction. This atomically restores the user instruction pointer (RIP), user stack pointer (RSP), user execution flags (RFLAGS), and drops the hardware privilege level back to Ring 3. Incoming Task B resumes execution at the exact instruction it was executing when previously suspended.
Figure 1.2: Anatomical Breakdown of the Scheduler & Dispatcher Execution Cycle
Tracing the privilege transitions, policy evaluation, and silicon-level context swap between Outgoing Task A and Incoming Task B.
USER MODE (Ring 3) — Applications & Threads Task A (Outgoing Process) RIP: 0x4010a2 | Ring 3 Running Task B (Incoming Process) RIP: 0x4087fe | Ring 3 Resumed KERNEL MODE (Ring 0) — Supervisor Execution 1. Trap & Context Save APIC Timer / Syscall • Save RSP, RIP, RFLAGS • Push regs (struct pt_regs) 2. CPU Scheduler (Policy) pick_next_task() • Decrement quantum / charge CPU • Selects Task B from Ready queue 3. The Dispatcher (Low-Level Hardware Mechanism: switch_to) A. MMU Context Switch Reload CR3 with Task B Page Table (Bypassed if threads share same mm) B. Kernel Stack & TSS Switch RSP to Task B kernel stack Update TSS.sp0 for next interrupt C. Hardware Registers & TLS Set %fs/%gs MSR to Task B TCB Pop general registers from stack Trap / IRQ (Ring 3 → Ring 0) switch_to() IRETQ / SYSRETQ (Ring 0 → Ring 3)

2. Deconstructing Dispatch Latency: Direct vs. Indirect Costs

The total computational penalty incurred by a dispatch cycle is split between direct hardware overhead and indirect memory hierarchy penalties:

3. When to Schedule: Trigger Points & Preemption

CPU scheduling decisions arise under four distinct operational events:

  1. Running → Waiting (Blocked): The process initiates a blocking operation (e.g., synchronous file I/O, network packet wait, sleep(), or waiting on a semaphore/mutex).
  2. Running → Ready: The process is forcibly interrupted while actively executing instructions (e.g., an APIC hardware timer interrupt indicates its time slice has expired, or an external device interrupt unblocks a higher-priority task).
  3. Waiting → Ready: An asynchronous hardware event occurs (e.g., disk controller finishes DMA transfer, network packet arrives at the NIC), moving a previously blocked process into the Ready queue.
  4. Terminated: The running process completes execution by calling exit() or is aborted by the kernel due to an unhandled exception (e.g., segmentation fault SIGSEGV).

Preemptive vs. Non-Preemptive (Cooperative) Scheduling

Interactive Stepper: Preemptive vs. Cooperative Execution Models

Scenario Arc Process A (Compute-Bound Matrix Math) is executing. Process B (Interactive User Input) arrives in the Ready queue. Tracking how the scheduler handles CPU allocation.
Active CPU Entity Process A (Compute-Bound)
Quantum Status Timer: 8ms Remaining
CPU Core Mode User Mode (Ring 3)
Interactive Latency 0 ms (Process B Buffered)
READY QUEUE (FIFO / Priority) Process B (Interactive) State: Ready (Key Waiting) Process C (Background) State: Ready (Queued) PHYSICAL CPU CORE 0 Status: Executing Ring 3 Process A (Compute) Burst: Infinite Math Loop Quantum: 8ms Remaining BLOCKED QUEUE Awaiting Hardware Disk Read (FD 4)
Current Step Summary Process A runs uninterrupted on CPU Core 0, performing intensive matrix calculations. Process B enters the Ready queue awaiting interactive input processing.
What Is Happening

Process A executes unconstrained in User Mode. Process B arrives in the Ready queue after a keyboard keystroke triggers an asynchronous interrupt.

Why The System Does This

The CPU maintains maximum instruction throughput by keeping the execution pipeline full with Process A until an interrupt forces an intervention.

4. Conflicting Optimization Goals & Metrics

An operating system scheduler operates in an environment of inherent physical constraints and mutually incompatible performance objectives. No single scheduling algorithm can simultaneously maximize raw hardware throughput, eliminate user latency, guarantee absolute fairness, and maintain zero scheduling overhead. Designing or selecting a scheduler requires understanding the formal mathematical metrics that quantify performance and the trade-offs that govern their optimization.

1. Formal Mathematical Scheduling Metrics

To evaluate and compare scheduling algorithms rigorously, operating system architects define precise timing invariants across the lifecycle of a process:

Figure 1.3: Anatomical Process Lifecycle Timeline & Metric Intervals
Visualizing how Arrival, Ready Wait, Execution Bursts, I/O Blocks, and Completion map to Response, Wait, and Turnaround metrics.
Ready Queue Wait (T_wait 1) CPU Burst 1 Running Blocked (I/O Wait) Disk Read / NIC Ready (T_wait 2) CPU Burst 2 Terminates / exit() T_arrival T_first_dispatch T_completion T_response T_wait (1) T_wait (2) Total T_wait = Wait(1) + Wait(2) T_turnaround = T_completion − T_arrival (Total Elapsed Duration)

2. Fundamental Architectural Dilemmas & Trade-Offs

Optimizing an operating system scheduler is an exercise in managing engineering tensions along Pareto optimal frontiers:

A. Turnaround Time vs. Response Time

Algorithms that mathematically optimize average turnaround time (such as Shortest Job First / Shortest Remaining Time Next) do so by letting short jobs run to completion while ignoring long jobs. However, if an interactive process arrives while a compute job is underway, non-preemptive turnaround optimizers force the interactive task to wait, severely degrading response time.

Conversely, algorithms designed to minimize response time (such as Round-Robin with tiny time slices) frequently interrupt long jobs to service incoming interactive events. Because every context switch incurs dispatch latency and invalidates CPU cache hierarchies, the total turnaround time for all jobs degrades.

B. System Throughput vs. Latency (The Quantum Sizing Dilemma)

Consider a system where every context switch requires dispatch latency δ. If the scheduler assigns a time quantum of length q, the fraction of CPU time wasted purely on dispatch overhead (α) is:

α = δ / (q + δ)
C. Fairness vs. Prioritization & Starvation

Strict fairness implies that every ready task receives an equal share of CPU cycles (e.g., pure Round-Robin or Generalized Processor Sharing). However, operating systems must enforce real-world urgency:

3. Operational Environments & Optimization Target Matrix

Operational Arena Primary Optimization Targets Secondary Constraints & Tolerated Trade-offs
All Operating Systems • Fairness: Comparable processes receive comparable service.
• Policy Enforcement: Stated allocation rules are upheld.
• Starvation Freedom: No runnable task waits indefinitely.
Must minimize scheduling algorithmic complexity (O(1) or O(log N) runqueue operations).
Batch Processing Systems
(HPC, Scientific Compute, Compilers)
• Throughput: Maximize jobs completed per hour.
• Turnaround Time: Minimize time from submission to output.
• CPU Utilization: Keep execution units 100% busy.
Response time is completely irrelevant; long preemption delays are encouraged to minimize cache churn and context switch overhead.
Interactive & Desktop Systems
(Workstations, Laptops, Mobile OS)
• Response Time: Fast feedback to user events (<16–50 ms).
• Proportionality: Simple tasks respond immediately.
• Low Variance: Smooth UI frame rates without stutter.
Tolerates 2%–5% throughput loss and lower average turnaround time to support continuous preemption and time slicing.
Real-Time Embedded Systems
(Avionics, Robotics, Automotive ABS)
• Meeting Deadlines: Hard timing constraints must never fail.
• Predictability (Zero Jitter): Deterministic worst-case execution time (WCET).
Average throughput and hardware utilization are deliberately sacrificed; systems are intentionally over-provisioned to guarantee timing margins.