Attempt #1 left three holes: starvation, gaming, and stranded phase-changers. Two rule changes close all three.
8.3 Attempt #2: The Priority Boost
The simple idea: periodically throw everyone back to the top.
ATTEMPT #2
- Rule 5: After some time period S, move all the jobs in the system to the topmost queue.
One rule, two problems solved. No starvation: a job in the top queue shares via round robin (Rule 2), so everyone eventually gets service. And phase changes handled: a CPU-bound job that has turned interactive gets its priority boost priority boost Periodically (every S) moving all jobs to the top queue โ cures starvation and lets phase-changed jobs be re-learned. defined in ch. 8 โ open in glossary and is re-judged on current behavior. Try it:
| job | arrival | runtime | turnaround | response |
|---|---|---|---|---|
| A | 0 | 200 | 400 | 0 |
| B | 100 | 100 | 199 | 0 |
| C | 100 | 100 | 200 | 1 |
| average | 266.33 | 0.33 | ||
With boost off, interactive B and C (arriving at t=100) alternate at Q2 forever, and A โ long since sunk to Q0 โ receives exactly 0% of the CPU from t=100 on: starvation. Set boost S to 50: every 50 ms A is thrown back to the top queue and gets real slices (~20% share). Rule 5 in action.
Of course, Rule 5 introduces a knob: what should S be? Set it too high and long-running jobs still starve; too low and interactive jobs lose their edge.
Tip: Avoid voo-doo constants (Ousterhoutโs Law)
John Ousterhout called values like S voo-doo constants voo-doo constant A magic parameter (like the boost period S) that seems to need black magic to set correctly; avoid when possible (Ousterhout's Law). defined in ch. 8 โ open in glossary โ they seem to require black magic to set correctly. Avoid them when possible; unfortunately, as here, it often isnโt. The frequent result: a config file full of defaults that a seasoned admin could tweak โ and nobody ever does. Weโre left hoping the defaults work in the field.8.4 Attempt #3: Better Accounting
Now the gamers. The culprits are Rules 4a/4b themselves: they forget how much slice a job used whenever it relinquishes the CPU. The fix is memory โ track the allotment allotment The total CPU time a job may use at one priority level before demotion โ counted across all its bursts (Rule 4). defined in ch. 8 โ open in glossary a job has consumed at its level, across however many bursts:
ATTEMPT #3 โ 4a and 4b become one rule
- Rule 4: Once a job uses up its time allotment at a given level (regardless of how many times it has given up the CPU), its priority is reduced (it moves down one queue).
| job | arrival | runtime | turnaround | response |
|---|---|---|---|---|
| G | 0 | 100 | 120 | 0 |
| H | 0 | 100 | 200 | 9 |
| average | 160.00 | 4.50 | ||
G is the gamer: it computes 9 ms of every 10 ms slice, then issues a junk I/O. Under Rules 4a+4b it stays at Q2 and hogs ~80โ90% of the CPU while honest H sinks. Switch accounting to Rule 4: G's allotment is counted across bursts, it descends like everyone else, and the CPU splits fairly.
Whether the allotment is burned in one long run or a hundred sneaky 99%-bursts, the outcome is the same: down you go. The 99%-and-I/O attack buys nothing.
8.5 Tuning MLFQ And Other Issues
How many queues? What slice per queue? How often to boost? There are no easy answers โ only experience with workloads and tuning. A few patterns from real systems:
Varying time slices per level. High-priority queues hold interactive jobs โ short slices (โค10 ms) and quick alternation make sense there. Low queues hold CPU-bound work โ long slices (100s of ms) amortize switching:
| job | arrival | runtime | turnaround | response |
|---|---|---|---|---|
| A | 0 | 100 | 170 | 0 |
| B | 0 | 100 | 200 | 10 |
| average | 185.00 | 5.00 | ||
Both jobs take 10 ms turns at Q2, 20 ms turns at Q1, then settle into long 40 ms turns at Q0. High queues switch fast (interactive feel); low queues switch rarely (amortized batch work) โ most real MLFQs are tuned this way.
Real implementations. Solarisโs Time-Sharing class (TS) is
table-driven: ~60 queues, slices growing from 20 ms (high) to a few
hundred ms (low), boosts about every second โ and an administrator can
edit the table. FreeBSD (4.3) took the formula route instead:
decay-usage scheduling computes priority from recent CPU usage,
decayed over time โ the decay is the boost, delivered continuously.
Other wrinkles: many systems reserve the very top priorities for OS work
(user jobs can never reach them), and some accept user
advice advice Hints users/apps give the OS (nice, madvise, informed prefetching) โ the OS may use them for better decisions but needn't obey.
defined in ch. 8 โ open in glossary
โ nice nudges a jobโs priority up or
down from the command line.
Tip: Use advice where possible
The OS rarely knows whatโs best for every process, so interfaces that let users and applications drop hints are widely useful: the schedulerโsnice, the memory managerโs madvise,
file-system informed prefetching. The OS may ignore the hint โ thatโs
why itโs called advice โ but a good hint often makes a better decision.8.6 MLFQ: Summary
The refined rule set, complete:
MLFQ โ THE FIVE RULES
- Rule 1: If Priority(A) > Priority(B), A runs (B doesnโt).
- Rule 2: If Priority(A) = Priority(B), A & B run in round-robin fashion using the time slice of the given queue.
- Rule 3: When a job enters the system, it is placed at the highest priority.
- Rule 4: Once a job uses up its time allotment at a given level (regardless of how many times it has given up the CPU), its priority is reduced.
- Rule 5: After some time period S, move all the jobs in the system to the topmost queue.
Why MLFQ matters: it demands no a priori knowledge โ it observes and prioritizes accordingly, getting the best of both worlds: near-SJF/STCF performance for short interactive work, fairness and progress for long-running CPU-bound work. Thatโs why forms of MLFQ are the base scheduler in BSD UNIX derivatives, Solaris, and Windows NT and its successors. Multiple levels of queues; feedback sets the priority. History is its guide.
Homework: mlfq.py
The simulator reproduces everything on this page: rebuild the chapterโs figures, configure it to behave as pure round robin, craft a two-job workload that games old Rules 4a/4b (the-S flag) for 99%
of the CPU, and compute how often to boost (-B) so a
starving job is guaranteed 5% of the CPU. Also try -I:
which end of the queue should a job rejoin after I/O? Get it at
ostep-homework.Check yourself
1.Rule 5 (boost everyone to the top every S) solves two problems at once. Which two?
2.Why does the book call the boost period S a 'voo-doo constant'?
3.How exactly does rewritten Rule 4 defeat the 99%-of-a-slice-then-junk-I/O attack?
4.Why do real MLFQs give LOW-priority queues longer time slices (e.g., 10 ms up top, 100s of ms at the bottom)?
5.Solaris tunes MLFQ with an editable 60-queue table; FreeBSD 4.3 used a decay-usage formula instead. What does the decay naturally provide?