CPU Scheduling
IntermediateLesson 2 of 9 in Operating Systems Course
What you will learn
- Explain where scheduling decisions happen in the life of a process, tell preemptive from non-preemptive scheduling, and calculate the metrics used to compare scheduling policies.
- Explain when and how the scheduler picks the next jobPlace the CPU scheduler in the process life cycle, distinguish preemptive from non-preemptive scheduling, and name the kinds of schedulers an operating system can have.
- Calculate turnaround, response and waiting timeDraw a timeline of a schedule and calculate turnaround time, response time, waiting time, throughput and CPU utilization, and explain which goal each metric serves.
- Explain when and how the scheduler picks the next job
- Schedule a set of jobs under the classic policies, calculate how each one performs, and explain why each policy suits some workloads and fails others.
- Schedule jobs with FIFO and shortest job firstApply first-in, first-out and non-preemptive shortest-job-first scheduling, explain the convoy effect, and estimate run times that are not known in advance.
- Preempt for shorter jobs and overlap I/OApply shortest time-to-completion first, explain when preemption improves turnaround, and keep the CPU busy by running other jobs while one waits for I/O.
- Use round robin for fast responseApply round-robin scheduling, explain its effect on response and turnaround time, and choose a sensible time slice.
- Use priorities without starving jobsApply priority scheduling, read the different priority scales used in practice, and prevent starvation and priority inversion.
- Schedule jobs with FIFO and shortest job first
- Explain how a multi-level feedback queue learns from a job's behavior to approximate shortest-job-first while keeping interactive jobs responsive, and how its rules are tuned against starvation and gaming.
- Trace jobs through a multi-level feedback queueState the basic rules of the multi-level feedback queue and trace how CPU-bound, short and interactive jobs move between its queues.
- Tune MLFQ against starvation and gamingExplain the problems of the basic MLFQ rules, fix them with priority boosts and better accounting, and choose its parameters.
- Trace jobs through a multi-level feedback queue
- Explain proportional-share scheduling, calculate the shares that lottery, stride and Linux's fair schedulers give, and compare their strengths.
- Share the CPU by lotteryRepresent shares with tickets, run a lottery draw, predict how close lottery scheduling comes to the target shares, and use ticket currency, transfer and inflation.
- Get exact shares with stride schedulingCalculate strides and pass values, trace a stride schedule, and compare stride with lottery scheduling.
- Explain Linux's fair schedulers: CFS and EEVDFCalculate time slices and virtual runtime under the Completely Fair Scheduler, relate nice values to weights, and describe how EEVDF chooses the next task.
- Share the CPU by lottery
- Explain how caches, coherence and affinity change scheduling on a multiprocessor, and compare single-queue and multi-queue designs that balance load across CPUs.
- Explain what changes with several CPUsDescribe multiprocessor hardware as the scheduler sees it: private caches, cache coherence, the need for locking, cache affinity and different kinds of cores.
- Compare single-queue and multi-queue multiprocessor schedulingCompare single-queue and multi-queue multiprocessor schedulers, recognize load imbalance, and fix it with migration and work stealing.
- Explain what changes with several CPUs
- Read how Linux, Windows and other systems combine priorities, feedback and proportional sharing, control them with standard tools, and choose and evaluate a policy for a given workload.
- Control the Linux schedulerDescribe Linux's scheduling policies and their history, and use nice values, real-time policies, CPU affinity and control groups.
- Read Windows and other systems as MLFQ-style schedulersCalculate Windows base priorities, explain dynamic priority boosts and their decay, and recognize the same feedback ideas in other systems.
- Choose and evaluate a scheduling policy for a workloadMatch scheduling policies to workloads, compare them on one example, and choose a method for evaluating a scheduler.
- Control the Linux scheduler
CPU Scheduling study guide
1Decide who runs and measure the result
A CPU can run one thread at a time, yet a busy system may have many threads ready to use it. The scheduler decides whose work advances at each opportunity. To judge that choice, follow both the process states and the clock: a quick first turn, an early finish, and a high rate of completed work describe different successes. A timeline makes those differences visible and gives you the evidence for comparing policies.
1.1 From a ready job to a running job
A CPU scheduler chooses the ready process or thread that runs next on a CPU. The rule it follows is its policy. The dispatcher carries out the choice by switching execution to the selected job. A different policy can use the same switching mechanism.
Follow the state changes
A running job owns a CPU. A ready job could run but is waiting for a turn. A blocked job is waiting for an event, such as the completion of a disk read. Only ready jobs are candidates for selection.
Suppose an editor starts a disk read while a compiler is ready. The editor moves from running to blocked, so the compiler can run. When the read finishes, an interrupt makes the editor ready again. It does not necessarily run immediately: the policy must choose between it and other ready work.
Scheduling decisions occur when a job blocks, finishes, becomes ready, or is interrupted and returned to ready. Voluntary yielding also releases the CPU. Preemption means taking the CPU from a job that could have continued running; the job keeps its saved state and can resume later.
| Policy style | When a running job gives up the CPU | Effect of a new ready job |
|---|---|---|
| Non-preemptive | It blocks, yields, or finishes | It waits for a decision opportunity |
| Preemptive | It can also lose the CPU while still able to run | It may displace the current job |
A timer interrupt lets the kernel enforce a time slice, even if a program loops without yielding. A completed I/O can also trigger preemption by making a more favored task ready. Linux, Windows, and macOS use preemptive scheduling.
Work comes in bursts
Programs alternate between CPU bursts, when they compute, and periods spent waiting for I/O. A CPU-bound program such as an encoder has long CPU bursts. An I/O-bound program such as an editor often computes briefly and then waits. Giving an editor prompt service after a keypress can help responsiveness without giving it much total CPU time.
The dispatcher saves the old task's state, restores the chosen task's registers, and resumes its instruction stream in user mode. Dispatch latency is the time taken to hand over execution. Switching also disturbs caches and the translation lookaside buffer, or TLB, which caches address translations. The next task may need to refill them, so register-saving time is only part of the cost.
Three scheduling timescales
The long-term scheduler admits submitted jobs and controls how many are in the system. The medium-term scheduler can suspend whole processes by swapping them out of memory and later restoring them. The short-term scheduler selects the next ready task and acts frequently. General-purpose systems rely mainly on this last role; batch systems and clusters may also control admission explicitly.
Start a policy comparison with simple CPU-only jobs and known lengths. Then vary one feature: unequal lengths expose FIFO's delays, later arrivals expose the limits of non-preemption, I/O creates opportunities to overlap work, and unknown lengths require prediction or feedback.
Key Takeaways
- Only ready jobs can be selected to run.
- A scheduling policy chooses; the dispatcher performs the switch.
- Preemption preserves a task so it can resume.
- Timer and device interrupts can create scheduling opportunities.
- Frequent switching costs both dispatch time and cache locality.
- Admission, swapping, and CPU selection act on different timescales.
1.2 Read times from a schedule
A schedule tells you when jobs run. A metric tells you which part of that schedule matters. Record each job's arrival, first run, and completion before calculating any averages.
Separate the three times
Turnaround time measures the whole interval from arrival to completion. Response time measures the delay until the first run. Waiting time adds every interval spent ready but not running.
For a CPU-only job, turnaround equals waiting plus CPU run time. With I/O, blocked time is also part of turnaround, so subtracting CPU time alone would overcount waiting. Response uses the first-run definition here; a user-facing service may separately measure time to its first output.
Work through a FIFO timeline
A arrives at 0 ms and needs 8 ms of CPU time. B arrives at 1 ms and needs 4 ms. C arrives at 2 ms and needs 2 ms. With no switching cost, FIFO gives:
- A: 0–8 ms.
- B: 8–12 ms.
- C: 12–14 ms.
| Job | Turnaround | Response | Waiting |
|---|---|---|---|
| A | ms | ms | 0 ms |
| B | ms | ms | 7 ms |
| C | ms | ms | 10 ms |
The average turnaround is ms. Average response and waiting are both ms. They coincide because each job runs once without interruption. Under preemption, a job may wait again after its first turn, so waiting can exceed response.
Measure the whole workload
Throughput counts completed jobs per unit of elapsed time. The schedule completes three jobs in 14 ms, about 214.29 jobs/s. CPU utilization is busy CPU time divided by elapsed time; it is 100% here because the CPU never idles. Specify the measurement interval: adding an idle period after completion would change both figures.
High utilization can still accompany long waits. It also need not mean all busy time is useful application work: real measurements may count switching and other kernel work as busy time. State whether an overhead calculation measures busy time or useful work.
Choose what to optimize
Batch work often values average turnaround. Interactive work values a quick response. Shared services need fair access as well as throughput. Starvation means an eligible job waits indefinitely, so a good average alone cannot establish fair treatment.
Inspect individual delays and the tail of the distribution. A 99th-percentile response time describes the delay at or below which roughly 99% of observations fall; a few severe delays can disappear inside a low average. Always subtract the job's own arrival time, and count its running intervals as service, never as waiting.
Key Takeaways
- Turnaround ends at completion; response ends at the first run.
- Waiting counts ready intervals and excludes blocked intervals.
- Subtract each job’s arrival before averaging.
- Throughput and utilization depend on the observation interval.
- Inspect tail delays and starvation alongside averages.
2Trace the classic scheduling policies
The same ready jobs can finish in very different orders under different policies. FIFO follows arrival, shortest-job policies favor small CPU demands, round robin rotates turns, and priority scheduling follows a rank. Build the timeline from the rule at each decision point, then calculate the outcome. Later arrivals, blocked jobs, and switching costs reveal trade-offs that a single ordering of jobs cannot show. Hand traces ignore switching cost unless specified.
2.1 FIFO and the shortest ready burst
FIFO, also called first come, first served or FCFS, runs ready jobs in arrival order. It is non-preemptive: a later arrival cannot interrupt a running CPU burst. For CPU-only examples, each selected job runs to completion.
See the convoy effect
A, B, and C arrive together. A needs 60 ms; B and C need 6 ms each. With A first, FIFO runs A 0–60, B 60–66, and C 66–72. Turnaround times are 60, 66, and 72 ms, averaging 66 ms. The small jobs spend most of their lives waiting behind A. This is the convoy effect.
Shortest job first (SJF) selects the shortest next CPU burst among ready jobs and does not preempt it. With the same arrivals, it runs B 0–6, C 6–12, then A 12–72. Average turnaround becomes ms. Total CPU work is still 72 ms; the improvement comes from finishing short jobs sooner.
Why does this help? If a long burst runs immediately before a short burst, both jobs wait for all earlier work. Swapping just these two cuts the short job's delay by the long burst's length while increasing the long job's delay only by the short burst's length. Repeating such swaps puts the bursts in ascending order. With all jobs arriving together and known lengths, SJF minimizes average turnaround and waiting; first-run response also equals waiting in this non-preemptive CPU-only case.
Restrict the choice to ready jobs
Now A arrives at 0 ms, while B and C arrive at 10 ms. At 0 ms, A is the only choice. Non-preemptive SJF runs A 0–60, B 60–66, C 66–72. Turnaround times are 60, 56, and 62 ms, averaging about 59.33 ms. SJF cannot act on a future job or interrupt a burst it already started.
| Policy | Information used | Main weakness |
|---|---|---|
| FIFO | Arrival order | Short work can wait behind long work |
| SJF | Predicted next burst length | Long work can be postponed by repeated short arrivals |
FIFO avoids starvation when jobs ahead eventually release the CPU. SJF can starve a long waiting job if short work keeps arriving.
Predict a burst
The next CPU burst is usually unknown. An exponential average combines the latest measured burst with the previous prediction:
Here, is the measured burst, the previous prediction, and a weight between 0 and 1. Starting with 10 ms and using , a measured 6 ms gives a prediction of 8 ms. A subsequent 4 ms gives 6 ms; another 6 ms leaves it at 6 ms. At zero weight, measurements have no effect; at weight one, only the newest measurement matters. The predicted quantity is CPU burst length, not program file size or line count.
Key Takeaways
- FIFO uses arrival order and can create a convoy.
- SJF chooses the shortest burst already ready.
- SJF’s average-time optimum requires the relevant workload assumptions.
- Later arrivals cannot interrupt non-preemptive SJF.
- Exponential averaging balances recent measurements with past predictions.
- A stream of short jobs can starve a long SJF job.
2.2 Interrupt a long job and overlap I/O
Shortest time-to-completion first (STCF), also called shortest remaining time first or SRTF, is preemptive SJF. At an arrival or completion, compare the CPU time each ready job still needs. A newcomer interrupts the current job when it needs less time than the current job has left.
Compare remaining time
A needs 60 ms and arrives at 0 ms. B and C each need 6 ms and arrive at 10 ms. A runs for 10 ms, leaving 50 ms. At 10 ms, B wins because 6 ms is less than 50 ms; C has the same length, so arrival order and then the name break the tie.
The timeline is A 0–10, B 10–16, C 16–22, A 22–72. Turnaround times are A 72 ms, B 6 ms, and C 12 ms, averaging 30 ms. Response times are 0, 0, and 6 ms, averaging 2 ms. Non-preemptive SJF on these arrivals averages about 59.33 ms turnaround and 35.33 ms response.
Always subtract service already received. Comparing a new 12 ms burst with A's original 60 ms would be wrong if A had only 5 ms left. STCF would finish A first. With known CPU-only lengths and zero switching cost, STCF minimizes average turnaround across arbitrary arrivals. Long jobs can still starve under a continuing stream of shorter work.
When all CPU-only jobs arrive together, SJF and STCF give the same ordering. The running shortest job only gets shorter, so no waiting job becomes a reason to preempt it.
Use another job's blocked time
A blocked job cannot use the CPU. Its I/O wait is an opportunity to run other ready work, even under a non-preemptive policy. Preemption matters again when completed I/O makes a short burst ready while another job is running.
Consider A with five 10 ms CPU bursts and a 10 ms disk read after each of its first four bursts. B needs one 50 ms CPU burst. Both arrive at 0 ms. If the CPU idles during A's reads and delays B until A finishes, A completes at 90 ms and B at 140 ms.
Treat each returning burst of A as short ready work:
- A runs 0–10; its read occupies 10–20 while B runs.
- A returns for 20–30; B has 40 ms left, so A's 10 ms wins.
- B runs 30–40 during the next read. The alternation repeats.
- A's last burst runs 80–90; B finishes its last 10 ms during 90–100.
A still finishes at 90 ms, but B finishes at 100 ms. The CPU performs 100 ms of work in 100 ms of elapsed time. Disk activity and CPU activity overlap; the CPU does not execute A while A is blocked. A device interrupt marks A ready when each read ends, after which STCF compares its new burst with B's remaining work.
Key Takeaways
- STCF compares remaining CPU demand at decision points.
- Preemption can let later short jobs finish much earlier.
- With simultaneous CPU-only arrivals, SJF and STCF agree.
- A blocked job releases the CPU so other work can overlap its I/O.
2.3 Rotate turns with round robin
Round robin (RR) keeps a FIFO ready queue and gives its front job at most one quantum, or time slice. A job that finishes or blocks leaves the CPU early. A job still ready when its slice expires moves to the back.
Trace turns before calculating times
A, B, and C arrive at 0 ms and each need 9 ms. With a 3 ms quantum, RR runs:
- First round: A 0–3, B 3–6, C 6–9.
- Second round: A 9–12, B 12–15, C 15–18.
- Final round: A 18–21, B 21–24, C 24–27.
First runs are at 0, 3, and 6 ms, so average response is 3 ms. Completions are at 21, 24, and 27 ms, so average turnaround is 24 ms. FIFO gives average response 9 ms and average turnaround 18 ms. Here RR starts everyone promptly but postpones the first completed job until 21 ms.
RR divides CPU access while jobs compete; it does not equalize completion times. A short job can finish on its first turn. With a fixed population of ready jobs and quantum , the wait between turns is at most without switching costs. Ongoing arrivals, other priority classes, and restricted CPU eligibility change the setting of that bound.
Handle arrivals at a slice boundary
Use arrival order, then alphabetical order for ties. A job arriving exactly when a slice expires joins before the preempted job. With A arriving at 0 ms needing 8 ms, B at 1 ms needing 4 ms, and C at 2 ms needing 2 ms, a 2 ms quantum gives A 0–2, B 2–4, C 4–6, A 6–8, B 8–10, A 10–14.
The final A interval contains two consecutive turns with no competitor. Response times are 0, 1, and 2 ms; turnaround times are 14, 9, and 4 ms. Their averages are 1 ms and 9 ms. When a blocked job returns in basic RR, it joins for a fresh turn; it does not reserve unused CPU time while blocked.
Make the quantum worth its cost
A very large quantum makes RR behave like FIFO for bursts shorter than that quantum. A tiny quantum creates many dispatches and repeated cache disruption. In a simple timer-based implementation the quantum is a whole number of timer periods.
For switching cost after each useful slice of length , the approximate overhead fraction is . A 0.1 ms switch costs about 9.1% with a 1 ms slice, but about 0.99% with a 10 ms slice. Longer slices amortize switching cost by spreading it over more useful work.
A common sizing heuristic is to let most bursts, for example about 80%, end within one slice. The right choice depends on measured burst lengths and the response delays the workload can tolerate. Count switches between tasks separately from timer events that simply let the same sole ready task continue.
Key Takeaways
- RR requeues an unfinished ready job after its quantum.
- Response can improve while average turnaround grows.
- Boundary arrival rules determine queue order.
- A short quantum increases switching overhead.
- Longer slices amortize dispatch and cache costs.
2.4 Priorities, aging, and lock dependencies
A priority scheduler runs the highest-priority ready job. Equal-priority jobs can follow FIFO or round robin. In a preemptive version, a newly ready higher-priority job takes the CPU immediately; in a non-preemptive version it waits until the current job releases it.
Read the direction of the scale
Priority numbers have no universal direction. Use smaller numbers for higher priority in the textbook examples here. Linux nice values follow that direction, but Windows priority levels and Linux real-time priorities use larger numbers for higher priority.
A, B, and C arrive at 0 ms with bursts of 4, 3, and 5 ms and priorities 2, 1, and 3. Non-preemptive priority scheduling runs B 0–3, A 3–7, and C 7–12. Their waiting times are 0, 3, and 7 ms, averaging about 3.33 ms; turnarounds are 3, 7, and 12 ms, averaging about 7.33 ms.
An internal priority comes from measured behavior, such as CPU usage or I/O waits. SJF derives rank from predicted burst length. An external priority comes from a person, such as an administrator who marks a backup as less urgent. Neither choice ensures that low-priority work receives time under a continuous supply of higher-priority work.
Raise priority with waiting
Aging gradually raises the priority of a waiting job. If the rank improves by one every 100 ms, a job starting at priority 20 reaches the top priority 0 after 2 s of ready waiting. Higher rank brings it into competition with favored work; tie rules determine turns among jobs at the same rank.
Starvation is a lack of progress caused by repeated selection of other work. Aging tackles it by changing rank over time. It serves a different purpose from predicting burst length: an old long job may deserve a turn even though its burst remains long.
Follow the dependency in priority inversion
A high-priority job H may be blocked on a lock held by low-priority job L. A medium-priority job M can preempt L. Now H cannot proceed, even though M has lower priority than H: L must run to release the lock first. This is priority inversion.
With priority inheritance, L temporarily inherits H's priority while it holds the lock H needs. M can no longer keep L off the CPU. L runs, releases the lock, and loses the inherited priority; H becomes ready and can run.
| Problem | Cause | Scheduling response |
|---|---|---|
| Starvation | Other ready work keeps outranking a job | Aging raises its rank with waiting |
| Priority inversion | Favored work waits for a lower-ranked lock holder | Inheritance raises the holder while the dependency lasts |
Raising H's own priority does not release its lock: H remains blocked. The key is to let the thread that can satisfy the dependency execute. In 1997, Mars Pathfinder encountered a priority-inversion problem that was addressed by enabling priority inheritance.
Key Takeaways
- Check which direction means higher priority.
- Aging raises waiting jobs to address starvation.
- A blocked high-priority task cannot run until its dependency is satisfied.
- Priority inheritance temporarily raises the lock holder.
3Learn a job’s behavior with feedback queues
The next CPU burst is rarely known in advance. A multi-level feedback queue learns from CPU use: new work gets an early chance, while sustained computation moves down to queues with less urgent service. Tracing both queue membership and consumed allotment explains that behavior. Periodic boosts and careful accounting then address the two main failures: work that waits too long and work that yields strategically to keep its rank.
3.1 Move jobs through feedback queues
A multi-level feedback queue (MLFQ) combines priorities with round robin. Its goal is to approximate short-job service and keep interactive work responsive without being told each job's future run time. Recent CPU use supplies the feedback.
Apply the queue rules
Each ready job belongs to one priority queue. The basic accounting version follows four rules:
- Choose the highest-priority queue containing ready work.
- Within that queue, rotate equal-priority jobs with round robin.
- Put a new job in the top queue.
- Demote a job when it consumes its CPU allotment at its current level, counting usage across all turns and I/O interruptions.
A quantum limits one turn. An allotment limits total service at a priority level. A level may allow several quanta before demotion. At the bottom level a demotion leaves the job in the bottom queue.
This gives a new job a chance to finish quickly. A job that repeatedly consumes CPU time reveals a longer demand and moves down. A short burst that blocks consumes little allotment, so interactive jobs tend to stay high for longer.
Trace a long job and a newcomer
Use queues Q2, Q1, and Q0, with Q2 highest, 10 ms quanta and 10 ms allotments at each level, and no boost during this trace. A needs 200 ms and arrives at 0 ms. It runs in Q2 from 0–10, in Q1 from 10–20, and in Q0 afterward.
At 100 ms, A has received 100 ms of CPU time. B arrives needing 20 ms. B enters Q2 and preempts A, then runs 100–110 in Q2. It has consumed that level's allotment and drops to Q1. Q1 outranks A's Q0, so B runs 110–120 and finishes. A resumes at 120 and completes its remaining 100 ms at 220 ms. B's turnaround is 20 ms despite arriving after a much longer job.
Track A's remaining work while B runs: it stays at 100 ms. Elapsed time advances during B's service, but A receives no CPU service then.
Separate waiting from CPU consumption
An editor that runs for 1 ms and waits for input uses a 10 ms allotment over ten such bursts. Blocked time does not count against the CPU allotment. Under strict accounting, those short bursts still add up, so the editor eventually moves down unless a priority boost resets it first.
An early MLFQ rule demoted only jobs that used a whole uninterrupted quantum. A job that yielded just before expiry kept its priority. That rule made interactive work responsive but also allowed deliberate gaming. Counting cumulative CPU use closes that loophole.
Lower queues run whenever all higher queues have no ready work. A higher-priority job that is blocked does not prevent lower-priority jobs from using the CPU. Under sustained high-priority demand, lower work needs an additional anti-starvation mechanism, supplied by periodic boosts.
Key Takeaways
- MLFQ learns from observed CPU use, without knowing future burst lengths.
- The highest ready queue wins; equal-priority jobs rotate.
- New jobs enter at the top.
- Quantum and allotment control different parts of the schedule.
- Cumulative CPU accounting includes service before a job blocks.
3.2 Prevent starvation and strategic yielding
Feedback creates useful separation, but a job's past behavior can stop representing its current needs. A long computation may later become interactive. Meanwhile, a bottom-queue job can wait indefinitely if higher queues stay busy.
Reconsider every job periodically
A priority boost moves all jobs back to the top queue after a period . It lets waiting jobs share the top queue and lets the scheduler learn changed behavior afresh. A boosted CPU-bound job later consumes its allotment and moves down again.
Consider the early MLFQ rules: two interactive jobs always yield before a full 10 ms slice and together keep Q2 busy. A CPU-bound job waits in Q0. Without boosts it gets no service. In a repeating schedule where a boost every 100 ms gives it one 10 ms top-level turn before it is demoted and higher work fills the rest, it receives 10% of the CPU. That percentage comes from this trace; a boost does not assign a general fixed share.
A long boost period leaves long waits between opportunities. A very short period repeatedly resets CPU-bound work and weakens the advantage of interactive work. Designs have used periods around one second, but the period is a tuning choice based on workload behavior and response goals.
Account across yields
A separate problem is gaming. Under the early full-slice rule, a program can run for 9 ms, request a 1 ms I/O, and repeat. It never uses a whole 10 ms slice at once, so it retains top priority while consuming nearly all available CPU time.
With a cumulative 10 ms allotment, the first burst consumes 9 ms and leaves 1 ms. After the I/O, one further millisecond exhausts the allotment, and the job is demoted during its second burst. An I/O request changes state; it does not erase service already received. The accounting rule prevents this tactic even without a boost.
| Mechanism | What it changes | Problem it addresses |
|---|---|---|
| Allotment accounting | Adds CPU use across turns | Strategic yielding before expiry |
| Periodic boost | Restarts priority learning | Starvation and changed behavior |
Tune the levels
Choose the number of queues, each queue's quantum, each level's allotment, and the boost period. Top queues usually use short quanta, for example 10 ms, to alternate quickly among interactive jobs. Lower queues can use much longer quanta, sometimes hundreds of milliseconds, to reduce switching during sustained computation.
Allotment can grow independently of quantum. A level with a 5 ms quantum and 15 ms allotment can allow three full turns before demotion. Increasing allotment delays classification as long-running; increasing quantum lengthens an individual turn.
Solaris's time-sharing design illustrates table-based feedback: its classic configurable table has 60 priority levels, with quanta ranging from about 20 ms at high priority to a few hundred milliseconds at low priority. Classic BSD designs instead computed priority from recent CPU usage that decays over time. Both let old CPU use lose influence, so a thread's distant history does not permanently determine its treatment.
Key Takeaways
- Boosts reopen service opportunities and let changed behavior be relearned.
- A boost does not give a permanent high priority or fixed CPU share.
- Cumulative accounting prevents the yield-before-expiry tactic.
- Short top-level quanta support response; longer low-level quanta reduce overhead.
- Allotment can span several turns at one level.
- Feedback can use priority tables or decaying CPU-use formulas.
4Divide CPU time by shares
A fair-share scheduler starts with an entitlement: how much of the contested CPU each runnable job should receive. Tickets express that entitlement in lottery and stride scheduling; weights express it in Linux’s fair class. The selection rules differ in how closely short intervals follow the intended split and how they favor prompt service. Follow the accounting to see why a larger weight earns more real CPU time while its virtual progress grows more slowly.
4.1 Draw weighted turns with lottery scheduling
Proportional-share scheduling aims to divide CPU service according to assigned shares. In lottery scheduling, each runnable job owns tickets. A random winning ticket chooses the job for the next equal-length slice. A job's chance of winning is its tickets divided by all runnable tickets.
Locate a winning ticket
A holds 100 tickets, B 50, and C 250. Number the 400 tickets from 0 through 399. A owns 0–99, B owns 100–149, and C owns 150–399. A draw of 300 selects C.
A list-based implementation draws a number and accumulates ticket counts. The running totals are 100 after A, 150 after B, and 400 after C. Stop at the first total strictly greater than the draw. Using “greater than or equal” would assign boundary tickets incorrectly: draw 100 belongs to B. Putting larger ticket holders first reduces the average length of this list search.
Distinguish expectation from a guarantee
With A holding 75 tickets and B 25, A's expected share is 75%. In 100 independent equal-slice draws with both runnable, A expects 75 wins. The standard deviation is:
slices.
A result around 71–79 wins is within roughly one standard deviation of the mean; it is not a guaranteed range. Short runs can depart markedly from the intended 3:1 ratio. As the observation window grows, the relative random variation becomes smaller. Lottery provides probabilistic fairness, not an exact finite-window split or a bounded wait for the next turn.
For two equal-ticket jobs with equal CPU demands, the unfairness metric U divides the earlier completion time by the later one. The ratio reaches 1 when they complete together and is smaller when one completes much earlier. This completion ratio is especially variable for short lottery-scheduled jobs.
Allocate tickets across users and dependencies
Ticket currency lets a user divide an assigned global entitlement locally. User X has 200 global tickets and assigns 300 local tickets to X1 and 100 to X2. Of 400 local tickets, X1 has three quarters, giving 150 global tickets; X2 gets 50. User Y has 100 global tickets for Y1. The total is 300, so X1 receives an expected 50%, X2 about 16.67%, and Y1 about 33.33% of the contested CPU.
Ticket transfer temporarily lends a client's tickets to a server doing work for it, then returns them when the request completes. This follows the work dependency. Ticket inflation lets cooperating jobs increase their own ticket counts to request more service. It requires trust or limits: unrestricted inflation lets a greedy job dilute everyone else's shares.
Lottery needs ticket totals but no record of how far each job is ahead in service. A new job simply contributes its tickets to the runnable pool. Assigning the tickets remains a policy choice; the random draw enforces that choice in expectation. A blocked job's tickets do not compete for a slice it cannot use.
Key Takeaways
- A ticket fraction gives a job’s chance of winning each draw.
- Cumulative lookup stops when the total exceeds the winning number.
- Lottery shares are expected proportions, with short-run variation.
- Ticket currency preserves a user’s global entitlement while dividing it locally.
- Transfer follows a dependency; unrestricted inflation can dilute others’ shares.
4.2 Keep service on track with stride values
Stride scheduling turns ticket shares into a deterministic sequence. Each runnable job has a stride, calculated as a large constant divided by its tickets, and a pass value that records its weighted progress. Choose the lowest pass, run that job for one equal-length slice, then add its stride to its pass.
Calculate the strides
A has 50 tickets, B 25, and C 100. With constant 1,000, their strides are 20, 40, and 10. More tickets produce a smaller stride. A favored job's pass rises slowly, so it becomes the minimum more frequently.
Start all passes at zero and break ties alphabetically. Keep the tuple in the fixed order A, B, C:
| Passes before selection | Job selected | Passes after its slice |
|---|---|---|
| (0, 0, 0) | A | (20, 0, 0) |
| (20, 0, 0) | B | (20, 40, 0) |
| (20, 40, 0) | C | (20, 40, 10) |
| (20, 40, 10) | C | (20, 40, 20) |
| (20, 40, 20) | A | (40, 40, 20) |
| (40, 40, 20) | C | (40, 40, 30) |
| (40, 40, 30) | C | (40, 40, 40) |
Only the selected job's pass changes. In seven slices, A receives two turns, B one, and C four: the exact ratio 50:25:100. At the shared pass value of 40, each has advanced through the same amount of weighted service. Between such alignments, short-window shares need not be exact.
Keep real service and virtual progress separate
The pass is an ordering value, not elapsed time or a count of tickets. B's large stride makes it wait while A and C catch up in pass. It still receives its intended one-seventh share over this repeating cycle. Adding the ticket count to the pass would reverse the design: the highest-ticket job would make the biggest jump and be selected less often.
The constant controls the scale of strides, not the intended ticket ratios. Integer implementations need enough precision to avoid distorting those ratios through rounding. Comparisons and updates must use a consistent scale.
Place a newcomer sensibly
Suppose established jobs have passes around 4,000. A new job starting at zero would win repeatedly while catching up, even if it had few tickets. Initialize its pass relative to current system progress, such as the minimum current pass, so it does not collect service for time before it arrived.
| Lottery | Stride |
|---|---|
| Random draws enforce expected shares | Lowest-pass selection tracks service deterministically |
| Ticket counts are enough for selection | Every job needs a pass and stride |
| A newcomer simply adds tickets | A newcomer needs a suitable initial pass |
Linux virtual runtime uses the related idea of charging weighted progress for actual CPU service. In both designs, a higher entitlement makes that progress grow more slowly, keeping the task competitive for later service.
Key Takeaways
- Stride is the common constant divided by tickets.
- Select the minimum pass and add only that job’s stride.
- Larger ticket holdings make pass values grow more slowly.
- New jobs need pass values tied to current progress.
4.3 Weights, virtual runtime, and eligible deadlines
Linux's fair schedulers track weighted CPU service. The Completely Fair Scheduler (CFS), introduced in Linux 2.6.23 in 2007, chose the runnable task with the smallest virtual runtime. Linux began replacing that selection rule with EEVDF in 6.6 in 2023.
Divide service by weight
A nice value ranges from −20, most favored, to +19, least favored. Nice 0 has weight 1024. Each step toward a larger nice value reduces weight by a factor of about 1.25. Example weights are 3121 at nice −5, 820 at nice 1, 335 at nice 5, 110 at nice 10, and 15 at nice 19.
Nice 0 against nice 1 gives , about 55.5% against 44.5%. Shares depend approximately on the difference in nice values: and are both about 3.05. Nice +19 competing with nice 0 still receives about 1.4% in this simple two-task model.
Classic CFS distributes a target scheduling latency by weight:
For weights 1024, 1024, and 335, the total is 2383. A 48 ms target period gives about 20.63 ms to each heavier task and 6.75 ms to the lighter one. These are illustrative parameters, not universal kernel defaults. With four equal tasks, 48 ms gives 12 ms each. With ten, 4.8 ms each would fall below an illustrative 6 ms minimum granularity, so the period must grow.
Charge virtual progress
CFS increases a running task's virtual runtime by:
A weight-1024 task gains one virtual millisecond per real millisecond. A weight-335 task gains about 3.06. Charging lighter tasks faster makes them wait sooner while heavier tasks receive more real service.
Classic CFS orders queued runnable tasks in a red-black tree by virtual runtime. Insertion and removal cost logarithmic time; a cached pointer to the leftmost node makes finding the minimum constant time. Blocked tasks leave this runnable tree. Wake-up placement limits old sleep from becoming an unlimited claim on catch-up service.
Choose among eligible tasks with EEVDF
Earliest Eligible Virtual Deadline First keeps weights and virtual service accounting. A task has positive lag when it is owed service and negative lag when it has received more than its entitlement. Tasks with nonnegative lag are eligible. EEVDF selects the eligible task with the earliest virtual deadline.
The deadline combines a request's virtual start with its requested service length scaled into virtual time. It is a service-ordering value, not a promised wall-clock completion time. A shorter requested slice can give an eligible task an earlier deadline and improve latency without increasing its long-run weight share. Current Linux supports slice requests through sched_setattr().
| Classic CFS selection | EEVDF selection |
|---|---|
| Smallest virtual runtime | Earliest virtual deadline among eligible tasks |
Key Takeaways
- Nice values determine relative weights for normal fair-class service.
- Virtual runtime grows inversely with weight.
- Classic CFS uses minimum virtual runtime and a cached tree minimum.
- Minimum granularity can extend the classic CFS period.
- EEVDF first checks eligibility, then compares virtual deadlines.
- Shorter EEVDF requests change latency, while weights determine shares.
5Place work across several CPUs
Several CPUs create more opportunities to run work, but placement now affects the cost of every choice. A thread may find warm caches on one core and distant memory on another. Ready queues must also support simultaneous decisions without corrupting shared state. Compare a single shared queue with separate per-CPU queues, then weigh the benefit of balancing work against the cost of moving it.
5.1 Account for caches and CPU topology
Several CPUs help only when there is parallel work. A single thread runs on one logical CPU at a time; several threads or independent programs can use several CPUs together. A scheduler must choose both which job runs and where it runs.
Share scheduling responsibility
In symmetric multiprocessing (SMP), CPUs run kernel code and make scheduling decisions for themselves. In asymmetric multiprocessing, one main processor coordinates scheduling while others run assigned work. Central control is simpler, but the main processor can become a bottleneck. General-purpose systems use SMP.
Simultaneous decisions require coordination. If two CPUs remove tasks from a shared ready queue, its updates must be synchronized so neither loses an entry nor selects the same queued task twice. More CPUs can increase contention for that shared structure.
Preserve useful cache contents
Cores have small fast caches, typically private L1 and L2 caches and a larger L3 shared by several cores. Temporal locality means reusing recently accessed data. Spatial locality means accessing nearby data, such as consecutive array elements.
A sorting thread on core 0 fills its caches and TLB. If it resumes there after a brief preemption, many needed entries may still be present. On core 3, it may need to fetch the same data again from a shared cache or memory. Cache affinity is the scheduler's tendency to keep a thread near the CPU it recently used. The exact benefit depends on what remains cached and on the cache-sharing topology.
Separate coherence from synchronization
Cache coherence prevents cores from indefinitely using conflicting cached copies of a single memory location. Bus snooping is one implementation idea: caches observe traffic and invalidate or update copies when another core writes. Larger systems can use other coherence mechanisms.
Coherence does not turn several instructions into one atomic operation. If two CPUs both read a queue length, alter entries, and write the length back, coherent individual reads and writes still allow harmful interleavings. The scheduler needs locks or another synchronization method for the whole update. A lock's shared cache line can itself move between cores as they compete for it.
Read the hardware layout
| Hardware feature | What the scheduler must consider |
|---|---|
| Simultaneous multithreading (SMT) | Logical CPUs on one core share execution units and caches |
| Non-uniform memory access (NUMA) | Memory near one socket can be faster to access than remote memory |
| Heterogeneous cores | Performance and efficiency cores offer different speed and energy trade-offs |
Intel calls its SMT implementation Hyper-Threading. Two busy threads on sibling logical CPUs contend for one core's resources; two logical CPUs are not equivalent to two independent physical cores. A NUMA migration may also move computation away from the memory it uses, even when cache warm-up is brief.
Moving a thread to an idle CPU can eliminate a long queue delay. Keeping it in place can preserve locality. Compare those costs using the actual workload: affinity is a performance preference, while an explicit affinity mask restricts which CPUs are allowed at all.
Key Takeaways
- Parallel speedup needs several runnable threads or programs.
- SMP lets several CPUs make scheduling decisions.
- Cache affinity preserves recently useful data and translations.
- Coherence does not make a multi-step update atomic.
- SMT, NUMA, and unequal core types affect placement costs.
5.2 Balance work without moving it constantly
A multiprocessor scheduler needs somewhere to hold ready work. A single shared queue makes work easy to find. Separate per-CPU queues reduce shared contention and preserve placement, but can leave one CPU overloaded while another is idle.
Compare queue designs
| Design | Main advantage | Main cost |
|---|---|---|
| Single queue (SQMS) | Any eligible idle CPU can take waiting work | Queue contention and weaker cache affinity |
| Multiple queues (MQMS) | Local decisions scale and retain affinity | Load imbalance needs active correction |
In single-queue multiprocessor scheduling, all CPUs draw from one shared queue. If jobs can run on every CPU, waiting work naturally fills an idle CPU. A shared lock protects the queue, so increasing CPU count can turn that lock into a bottleneck.
With five round-robin jobs A–E on four CPUs, one sequence assigns A, B, C, D in the first round; E, A, B, C in the second; and D, E, A, B in the third. A runs on CPU 0, then 1, then 2. The machine stays busy, but A repeatedly changes private caches. Affinity rules can retain most placements while moving selected jobs to balance work.
Notice unequal shares
In multi-queue multiprocessor scheduling, each CPU schedules its own queue. Suppose CPU 0 has A and C and CPU 1 has B and D, all CPU-bound and sharing locally in round robin. Initially each job gets half a CPU. When C finishes, A receives all of CPU 0 while B and D still receive half of CPU 1 each.
The machine remains fully utilized, but the jobs no longer get equal shares. If A also finishes, CPU 0 becomes idle even though B and D are ready. Moving one of them to CPU 0 gives both a full CPU and uses previously idle capacity.
With three equal CPU-bound jobs and two CPUs, equal long-run service is two-thirds of a CPU per job. A fixed placement leaves one job alone and two sharing. Periodically rotating which job is alone can approach the equal split. No one thread executes on both CPUs at once.
Decide when to migrate
Migration moves a task to another CPU's queue. Work stealing is a pull strategy: a CPU with little work examines a busier CPU's queue and takes work. Frequent checks create synchronization traffic; rare checks leave imbalance unresolved. Stealing also has to respect CPU eligibility and memory locality.
Compare expected saved waiting time with migration cost. A long queue on one CPU and an idle compatible CPU create a strong reason to move. A tiny temporary queue difference may disappear before cold-cache costs are recovered.
Linux's O(1) and CFS designs used per-CPU run queues; Linux balances across hardware scheduling domains, accounting for shared cores and sockets. The alternative BF Scheduler used a single queue. Users can constrain placement with taskset or sched_setaffinity() on Linux and SetThreadAffinityMask() on Windows. A restricted affinity mask can explain why a ready task does not use a CPU that appears idle.
Key Takeaways
- A shared queue finds waiting work easily but needs shared synchronization.
- Per-CPU queues preserve locality and reduce contention.
- Full CPU utilization can coexist with unequal per-job shares.
- Migration corrects imbalance at a cache and coordination cost.
- Work stealing pulls tasks toward CPUs with spare capacity.
- Affinity restrictions limit which idle CPUs can accept a task.
6Read system settings and evaluate a policy
Real schedulers combine several ideas: priority classes, weighted shares, feedback after waits, and placement across CPUs. A setting can change precedence, share, or allowed placement, so identify which control you are reading before predicting its effect. Linux and Windows provide concrete examples. To choose a policy, connect those mechanisms to a workload and test both average results and the delays experienced by its least favored jobs.
6.1 Use Linux scheduling controls
Linux schedules threads, called tasks in the kernel. A task's scheduling policy, nice value, group controls, and CPU affinity answer different questions about when and where it runs.
Read the policy first
| Policy | Selection behavior |
|---|---|
SCHED_OTHER and SCHED_BATCH | Normal fair-class service, weighted by nice |
SCHED_IDLE | Extremely low-priority normal work; nice does not affect it |
SCHED_FIFO | Real-time priority, without a same-priority quantum |
SCHED_RR | Real-time priority, rotating equal-priority tasks by quantum |
SCHED_DEADLINE | Deadline-class service above the other classes |
The normal policies have static priority 0. Real-time FIFO and RR use 1–99, where 99 is highest. A FIFO task continues until it blocks, yields, or is preempted by higher-priority work. RR adds turns among equal-priority tasks; sched_rr_get_interval() reports its quantum. These names describe Linux policies: normal fair scheduling is not the textbook equal-slice RR algorithm.
Change shares and placement
Nice runs from −20 to +19, with default 0. Larger values reduce fair-class weight, by roughly a factor of 1.25 per step. On a typical launch at nice 0, nice -n 10 make starts a build at nice 10; -n is an adjustment to inherited niceness. renice -n 5 -p 4321 sets the target process to nice 5. A user can make their own work less favored; increasing priority requires suitable privilege or an allowed resource limit.
taskset -c 0,1 ./app permits a program to run on CPUs 0 and 1. It does not create a second thread or promise either CPU exclusively. ps -o pid,cls,ni,rtprio,comm shows policy-related fields, while top helps observe priority, nice, and CPU consumption.
chrt -f 50 ./app selects FIFO real-time priority 50. Use real-time settings only in a controlled environment: a busy high-priority thread can delay the shell and essential system work. Default real-time bandwidth limits reserve some time for normal work, but do not make every such workload responsive. Precedence changes access to CPU service, not the speed of an instruction.
Distinguish group weight from quota
In cgroup v2, cpu.weight ranges from 1 to 10000, default 100, and divides contested fair-class CPU capacity among active sibling groups. Weights 200 and 100 give about two-thirds and one-third of one CPU when both groups are busy. If the second becomes idle, the first can use all available capacity.
cpu.max sets a bandwidth cap. The value "50000 100000" permits 50 ms of aggregate CPU time per 100 ms period, equivalent to half a CPU on average. Several threads can consume that budget in parallel and exhaust it sooner in elapsed time. A capped group can be throttled even when spare CPU capacity exists.
Place the names in history
Linux 2.6.0's O(1) scheduler used priority arrays and interactivity feedback. CFS arrived in 2.6.23 in 2007. Linux 6.6 in 2023 began the transition to EEVDF selection in the fair class. Since Linux 6.12 in 2024, sched_ext supports dynamically loaded BPF scheduling policies on configured kernels. If such a scheduler fails or stalls runnable work, the kernel restores built-in scheduling.
Key Takeaways
- Policy class determines precedence before nice is interpreted.
- Nice adjusts relative fair-class share; affinity restricts allowed CPUs.
- SCHED_IDLE ignores nice.
- A cgroup weight shares contested capacity; a quota caps aggregate CPU service.
- EEVDF changes fair-class selection, while sched_ext supports replaceable policies.
6.2 Follow Windows priorities and feedback
Windows schedules ready threads by priority from 0 through 31, with larger numbers higher. Equal-priority ready threads rotate in round robin. A newly ready higher-priority thread preempts a lower one without waiting for its quantum to end. Priority 0 belongs to the zero-page thread, which clears free pages.
Find base priority from two settings
A thread's base priority depends on its process's priority class and its own relative thread level. Process classes are Idle, Below normal, Normal, Above normal, High, and Realtime. Thread levels include Idle, Lowest, Below normal, Normal, Above normal, Highest, and Time critical.
| Process class | Thread level | Base priority |
|---|---|---|
| Normal | Normal | 8 |
| Normal | Highest | 10 |
| High | Normal | 13 |
| Realtime | Normal | 24 |
Time critical gives 15 outside Realtime class and 31 within it. Idle thread level gives 1 outside Realtime and 16 within it. These boundary levels are special table entries, so adding a fixed offset to every class would give wrong results.
Follow a temporary boost
Priorities 1–15 are the variable range; 16–31 are the real-time range. A thread starts with dynamic priority equal to base priority. For variable-range threads, events such as input or completion of a wait can temporarily raise dynamic priority. Threads with real-time base priorities receive no such boosts.
Suppose a thread with base priority 8 is boosted to 14. If it keeps computing, its dynamic priority falls one level after each complete quantum: 13 after the first, 12 after the second, and 8 after the sixth. The boost does not permanently change base priority and does not decay below that base.
This is feedback: recently unblocked work gets prompt service, while sustained CPU use loses the temporary advantage. Windows also uses temporary boosts for long-waiting ready threads to address starvation. Such a boost can help a low-ranked lock holder make progress, but it is a different mechanism from lock-specific priority inheritance.
Recognize related designs
Solaris's time-sharing class is a table-driven MLFQ design. FreeBSD's ULE uses per-CPU run queues and compares sleep with running behavior to identify interactive threads. These systems share the idea of adapting service to observed behavior without using identical queue rules.
macOS provides quality-of-service (QoS) classes, including user-interactive, user-initiated, default, utility, and background. They describe the urgency of work. On Apple silicon, QoS helps the system place work across performance and efficiency cores. Programs communicate the work's importance; the operating system chooses placement according to policy and available hardware.
Real schedulers combine feedback, priorities, fairness, and affinity. A Windows Realtime class has stronger precedence, not a guaranteed faster computation or a complete real-time correctness guarantee.
Key Takeaways
- Windows priority numbers increase with precedence.
- Base priority combines process class with relative thread level.
- Variable-range boosts decay one level per full quantum toward base.
- Real-time base priorities do not receive dynamic boosts.
- Solaris, ULE, and macOS use related ideas with different mechanisms.
- QoS communicates work urgency and helps guide placement.
6.3 Choose a policy and test its consequences
Choose the workload's objective before choosing a scheduler. A batch queue may value quick completion of many small jobs. An editor needs prompt service after input. A shared server needs a defensible allocation among users or groups.
Match the mechanism to the objective
| Workload need | Suitable mechanism | Trade-off to inspect |
|---|---|---|
| Low average batch turnaround | SJF or STCF with known or predicted bursts | Prediction error and starvation |
| Prompt interactive service | Preemption with feedback or latency-aware fair selection | Switching cost and tail delays |
| Proportional access among users | Tickets, strides, or group weights | Short-window fairness and group allocation |
| Long CPU-bound computation | Enough service per turn to amortize switching | Responsiveness of competing work |
| Time-critical device work | Appropriate real-time policies | Interference with other tasks |
Compare policies on identical inputs
A arrives at 0 ms needing 8 ms, B at 1 ms needing 4 ms, and C at 2 ms needing 2 ms. Ignore switching cost. FIFO runs A, B, C. SJF also starts A, but then runs C before B. STCF runs A 0–1, B 1–2, C 2–4, B 4–7, A 7–14. RR uses a 2 ms quantum with boundary arrivals queued before the expired task.
| Policy | Average turnaround | Average first-run response |
|---|---|---|
| FIFO | 10.33 ms | 5.67 ms |
| SJF | 9.67 ms | 5 ms |
| STCF | 7.33 ms | 0 ms |
| RR | 9 ms | 1 ms |
STCF wins both metrics on this workload because each newly arriving shorter job immediately runs. RR gives a quick start without knowing burst lengths. With equal-length simultaneous arrivals, FIFO can have lower average turnaround than RR. A ranking on one workload is evidence about that workload.
Choose an evaluation method
Deterministic modeling calculates a fixed trace by hand. It gives exact results for its inputs and makes decisions easy to inspect, but often leaves out dispatch and cache costs. Queueing models describe arrival and service patterns using distributions and predict average behavior.
For a stable system in steady state, Little's law links average population , arrival rate , and average residence time :
At 14 jobs/s and 0.5 s average time in the system, the average population is 7 jobs. Keep the boundary consistent: a ready-queue population needs time waiting in that queue, while a whole-system population needs time in the whole system. Convert units before multiplying.
Simulation executes a scheduler model, often against a recorded workload trace. It can test complicated arrival and blocking patterns, but its conclusions depend on the model and the chosen traces. Implementation measures the scheduler in a real operating system and includes actual overhead; it takes more effort and still needs representative workloads and repeatable measurements.
Inspect throughput, utilization, individual and tail delays, fairness, and energy use alongside means. On a multiprocessor, a busy machine can still divide service unevenly or waste time refilling caches. Compare policies using the same observation window and workload, then vary the workload features that matter to the intended users.
Key Takeaways
- Choose metrics to match the workload’s actual needs.
- Compare policies on identical arrivals and CPU demands.
- A workload-specific ranking does not establish a universal winner.
- Little’s law requires a stable system and matching population/time boundaries.
- Simulation depends on the model; implementation includes real overhead.
- Evaluate tail delays, fairness, throughput, and energy alongside averages.
CPU Scheduling examples
1From ready states to measured delays
11 steps
- 1IntroductionFollow the CPU handoff
A running job starts a disk read while another job is ready. The operating system can use that waiting period to advance other work. We will follow the state changes first (a job is ready, running, or blocked), then read a short CPU timeline to measure what each job experienced: response, waiting, and turnaround.
Keep two questions separate as you work: who can run now, and what delay should a metric count? A job may have a quick first turn but still finish much later.
- 2ExplanationChoose among jobs that can execute
The scheduler selects ready work. A running task already has a CPU; a blocked task must wait for its event. When the read starts, its caller becomes blocked and releases the CPU. When the read completes, it becomes ready again.
The completed read creates a selection opportunity. Whether the returning task runs immediately depends on the policy and on other ready work.
- 3ConnectionSeparate the choice from the switch
The policy chooses the next ready task. The dispatcher saves the old state, restores the chosen state, and resumes execution. Its latency and the next task’s cache refills cost time.
A timer can return control to the kernel even when a program never yields. A device interrupt can also make favored work ready. Preemption preserves the interrupted task for a later turn.
- 4ChallengeCan the waiting editor preempt?
An editor is blocked on input while a CPU-bound task runs. The editor has higher priority. Can it take the CPU before input arrives? Explain the state that matters.
---reveal---
No. The editor is blocked and cannot execute until its input event occurs. After input makes it ready, a preemptive priority scheduler can choose it. Priority ranks ready work; it does not remove the event dependency.
- 5ExampleDraw the service intervals
A arrives at 0 ms needing 5 ms, B at 2 ms needing 3 ms, and C at 4 ms needing 1 ms. They are CPU-only jobs. FIFO with no switching cost runs A 0–5, B 5–8, then C 8–9.
Record first-run times 0, 5, and 8 ms and completion times 5, 8, and 9 ms. These are timestamps. We next subtract each job’s own arrival to turn them into delays.
- 6ExampleMeasure completion delay
Turnaround runs from arrival through completion. For the timeline, A’s turnaround is 5 ms, B’s is 6 ms, and C’s is 5 ms.
The mean is:
ms.
B’s completion timestamp is 8 ms, but it has been in the system for only 6 ms because it arrived at 2 ms. Measuring every job from the beginning of the chart would overstate later arrivals’ delays.
- 7ExampleMeasure the first turn and total waiting
Response ends when the job first runs. A waits 0 ms for that first turn, B waits 3 ms, and C waits 4 ms. Their average response is about 2.33 ms.
Here response equals total waiting because each CPU-only job runs without interruption once selected. Under preemption, later ready intervals also count as waiting. A blocked I/O interval counts toward turnaround, but not toward ready-queue waiting.
- 8ChallengeCalculate a preempted job’s delays
Task R arrives at 1 ms, first runs at 4 ms, and finishes at 15 ms. It uses 6 ms of CPU with no I/O. Find response, turnaround, and waiting.
---reveal---
Response is 3 ms and turnaround is 14 ms. Subtracting 6 ms of service from turnaround gives 8 ms of total waiting. The initial 3 ms is only part of that waiting; another 5 ms occurs after the first turn.
- 9Key InsightMeasure the machine and the individual
Our FIFO trace finishes three jobs in 9 ms with no idle time. Its throughput is about 333.33 jobs/s and its CPU utilization is 100% over that interval. These machine-wide figures do not describe every job’s experience.
Inspect individual delays and a high percentile when users care about rare long waits. Fairness also asks whether ready work receives service, including jobs hidden behind a favorable average.
- 10ChallengeUse a blocked interval correctly
A task arrives at 0 ms, runs 0–2, waits for a device 2–7, waits ready 7–10, and runs 10–12 to finish. What are its response and waiting times?
---reveal---
Response is 0 ms because the task runs immediately. Waiting is 3 ms, the ready interval 7–10. Its 12 ms turnaround also contains 4 ms of CPU and 5 ms blocked. Calling all 8 ms outside CPU execution “waiting” would mix two process states.
- 11RecapCheck state before counting time
For a new trace, identify ready work at each event, record service intervals, then extract first-run and completion timestamps. Subtract arrivals before averaging. Keep blocked time separate when measuring ready-queue waiting.
2Build schedules from the policy rule
17 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionOne workload, several choices
- 2ExampleStart with arrival order
- 3ExampleLet short work finish first
- 4ChallengePredict the next burst
- 5ExampleStop the long job for a shorter arrival
- 6ExampleFinish the remaining work
- 7ChallengeCompare against what is left
- 8ConnectionLet blocked work create capacity
- 9ExampleRotate two equal jobs
- 10ExampleCalculate completion under rotation
- 11ChallengePlace a boundary arrival
- 12Key InsightPay for every handoff
- 13ExampleRead rank before running
- 14ExampleFollow the lock holder
- 15ChallengeChoose the useful priority change
- 16ChallengeChoose response or completion
- 17RecapTurn a rule into a timeline
3Trace feedback and catch gaming
12 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionLearn without a future burst length
- 2ExplanationUse one queue and two service limits
- 3ExampleLet sustained CPU use move down
- 4ExampleGive the newcomer its early chance
- 5ChallengePreserve an interrupted allotment
- 6ExampleExpose the early-yield loophole
- 7ExampleKeep the service history
- 8ChallengeCount only CPU consumption
- 9ExplanationGive old work a fresh opportunity
- 10ChallengeSeparate the two repairs
- 11ConnectionTune turns separately from classification
- 12RecapTrack the evidence that changes priority
4Account for proportional CPU service
16 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionStart with an entitlement
- 2ExampleRead the ticket intervals
- 3Key InsightExpect variation over short windows
- 4ChallengeConvert a local allocation
- 5ConnectionLet a service inherit the client’s claim
- 6ExampleCalculate inverse-ticket strides
- 7ExampleBring weighted progress back together
- 8ChallengePlace a new stride task
- 9ExplanationCharge real service in virtual units
- 10ExampleSplit a target period
- 11TipRead nice as a weight input
- 12ConnectionFind the minimum efficiently
- 13ExplanationFilter eligibility before deadlines
- 14ChallengeApply both EEVDF comparisons
- 15ChallengePreserve share while changing request size
- 16RecapSeparate allocation from selection
5Balance CPU load while keeping useful locality
11 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionA second CPU adds a placement choice
- 2ExplanationRecognize the available parallelism
- 3ExampleKeep a recently used working set
- 4ChallengeDoes coherence protect the whole update?
- 5ExplanationChoose a queue layout
- 6ExampleSpot unequal service on busy CPUs
- 7ExampleRotate placement to equalize service
- 8ChallengeUse an idle CPU legally
- 9Key InsightTrade recovery cost for saved delay
- 10ChallengeDecide whether the move saves time
- 11RecapCheck capacity, eligibility, and locality
6Read system controls and assess a scheduler
15 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionRead each control at its own layer
- 2ExplanationCheck the Linux policy class
- 3ExampleDistinguish share from placement
- 4ExampleApply group weights under competition
- 5ChallengeRead an aggregate quota
- 6ConnectionRecognize the scheduler generation
- 7ExplanationMap Windows settings to base rank
- 8ExampleLet a temporary boost decay
- 9ChallengeCheck the boost range
- 10ConnectionRecognize related feedback ideas
- 11ExplanationChoose the measurement that matters
- 12ExampleKeep Little’s law inside one boundary
- 13ChallengeRecover waiting from population
- 14ChallengeExplain an apparent performance win
- 15RecapConnect a setting to a measured effect
CPU Scheduling practice questions
Set 1: Read decisions and trace CPU service10 Practice Questions
Question 1 of 10
Show answerHide answer
Question 2 of 10
Show answerHide answer
Question 3 of 10
Show answerHide answer
Question 4 of 10
Show answerHide answer
Question 5 of 10
Show answerHide answer
Question 6 of 10
Show answerHide answer
Question 7 of 10
Show answerHide answer
Question 8 of 10
Show answerHide answer
Question 9 of 10
Show answerHide answer
Question 10 of 10
Show answerHide answer
Set 2: Track priorities, allotments, and feedback10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Set 3: Follow ticket and virtual-time accounting10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Set 4: Place work and interpret Linux controls10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
nice -n 8 ./worker. Under normal fair scheduling with busy peers, what does the command change?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
cpu.max set to "60000 100000". At the start of a fresh period, two threads in it run simultaneously on two CPUs. After how much elapsed running time do they exhaust the budget?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
SCHED_FIFO at priority 35, the other SCHED_RR at priority 45. Which has higher precedence?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Set 5: Compare system behavior and evaluate evidence10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
sched_ext BPF scheduler stalls a runnable task and the kernel detects the failure. What recovery does the interface provide?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Stuck on your own question? Homework Help
Key Terms
A
Aging
Allotment
Amortization
B
Base priority
Blocked state
Boost period
C
Cache affinity
Cache coherence
CFS
Control group
Convoy effect
CPU affinity
CPU burst
CPU scheduler
CPU utilization
CPU-bound job
cpu.max
cpu.weight
Cumulative accounting
D
Demotion
Deterministic modeling
Device interrupt
Dispatch latency
Dispatcher
Dynamic priority
E
EEVDF
Exponential averaging
F
FIFO
I
I/O overlap
I/O-bound job
Interactive job
L
Lag
Little’s law
Load imbalance
Long-term scheduler
Lottery scheduling
M
Medium-term scheduler
Migration
Minimum granularity
MLFQ
MQMS
N
Nice value
NUMA
P
Pass value
Preemption
Priority boost
Priority class
Priority inheritance
Priority inversion
Priority scheduling
Proportional share
Q
Quality of service
Quantum
Queueing model
R
Ready state
Red-black tree
Remaining time
Response time
Round robin
S
sched_ext
SCHED_FIFO
SCHED_RR
Scheduler gaming
Shortest job first
Simulation
SMP
SMT
Spatial locality
SQMS
Starvation
STCF
Stride
Stride scheduling
Switching overhead
T
Tail latency
Temporal locality
Throughput
Ticket currency
Ticket inflation
Ticket transfer
Turnaround time
U
ULE
V
Virtual deadline
Virtual runtime
W
Waiting time
Work stealing
Workload trace
About this lesson
Make your own Study Materials
Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.