CEUC301 · UNIT 9 OF 9 · FINAL CHAPTER
Specialized
Operating
Systems
Every mechanism this book has built — schedulers (2), locks (3), memory managers (5–6), file systems (7), I/O scheduling (8) — was designed for a machine that mostly just has to be fast. This closing chapter asks what happens to each of those mechanisms when the machine also has to be provably on time (RTOS), tiny (embedded), or battery-powered (mobile). Nothing here is a new subject; it's the same eight chapters, under new constraints.
The spine, under three new constraints. WriteWell's autosave transaction — the same four-block write from 7.2, guarded by the same freemap mutex from 7.3 — runs through all three sections here. 9.1 asks what happens when a higher-priority task needs that exact mutex while a medium-priority task is running. 9.2 asks what it costs to allocate that transaction's memory on a device with kilobytes, not gigabytes, to spend. 9.3 asks what it costs in battery to run that autosave at all, and what a phone's OS does differently with the memory and permissions around it. None of WriteWell changes; the machine underneath it does.
Fast Isn't the Goal. Provable Is.
2.2 asked how to keep a CPU busy and fair. A real-time OS asks a completely different question: can I prove, before the system ever runs, that this task will never miss its deadline — not "usually," not "on average," but never?
A general-purpose OS treats an occasional slow response as a minor annoyance. A real-time OS (RTOS) treats it as a correctness failure. If a car's anti-lock braking task is late, "the system was fast on average" is not a defense. This distinction — predictability over throughput — reshapes every layer, from how the kernel is built to how locks behave.
Hard, soft, and the kernel underneath
A hard real-time deadline miss is a system failure, full stop — braking controllers, pacemakers, industrial safety interlocks. A soft real-time deadline miss just degrades quality — a dropped video frame, a late audio sample. Both need an RTOS kernel that can bound its own overhead, which means design choices general-purpose kernels don't have to make:
- Bounded interrupt latency. The kernel can only disable interrupts for short, known-length windows — an interrupt held off for an unpredictable duration is a deadline that just became unpredictable too.
- A preemptible kernel. Even kernel-mode code must be interruptible by a higher-priority task almost everywhere, not just at user-mode boundaries.
- No unpredictable-latency memory tricks. 6.2's demand paging resolves a page fault in a time that depends on whether the disk has to be touched — inherently variable. Most RTOS kernels either skip virtual memory entirely or lock every page a real-time task uses into physical memory, trading 5–6's memory efficiency for 9.1's determinism.
Scheduling: 2.3's algorithms, now with a kernel that can actually deliver on them
2.3 already covered the two standard real-time scheduling policies in full: RMS (static
priority, shorter period first, Liu & Layland's sufficient bound U ≤
n(21/n−1)) and EDF (dynamic priority, nearest deadline first, optimal:
schedulable iff U ≤ 1). Nothing about the algorithms changes here. What
changes is that an RTOS kernel is specifically built so those bounds actually hold in practice —
the math in 2.3 assumes the scheduler itself takes negligible, bounded time to make its decision and
switch tasks. A general-purpose kernel where a context switch might occasionally take 50× longer
because of a page fault or a long interrupt-disabled region quietly invalidates every guarantee 2.3
proved on paper.
Priority inversion: when the wrong task blocks the most important one
Give a low-priority task a lock a high-priority task also needs, and something counterintuitive can happen: a third, medium-priority task that has nothing to do with the lock can end up delaying the high-priority one indefinitely. This is priority inversion, and it is not a bug in any one task — it is an emergent consequence of combining 2.3's priority scheduling with 3.2's mutexes, and it will happen on any system that combines the two without a specific countermeasure.
How it happens
Low-priority T_save acquires the freemap mutex (7.3) to start its autosave.
Before it finishes, a medium-priority background scan becomes ready and — being higher priority
than T_save — preempts it. T_save is not running,
but it still holds the mutex. Now a high-priority watchdog task needs that same mutex and
blocks. The watchdog cannot run until the mutex is free; the mutex cannot free until
T_save resumes; T_save cannot resume until the medium
task, which never even touches the lock, finishes on its own schedule. The highest-priority task in
the system is now effectively waiting on the lowest-priority one's scheduling luck.
The standard fix is the priority inheritance protocol (PIP): while a task holds a lock that a higher-priority task is blocked on, it temporarily inherits that higher priority — just long enough to finish the critical section and hand the lock over. This doesn't eliminate the wait; it bounds it to, at most, the length of one lower-priority critical section, no matter how many unrelated medium-priority tasks exist in the system.
Worked example 9.1
priority inversion, with and without PIPL (T_save, lowest priority):
1 unit of work, then acquires the freemap mutex for 4 units, then 1 more unit unlocked — arrives
at t=0. M (background scan, medium priority): 5 units, no lock — arrives
at t=1. H (watchdog, highest priority): 3 units, needs the same mutex —
arrives at t=2. Trace both scenarios.L runs [0,1), acquires the mutex. M arrives at 1
and, being higher priority, preempts L immediately — runs [1,6).
H arrives at 2, wants the mutex (held by L), blocks.
M is completely unaffected and runs to completion at t=6. Only then does
L resume its 4-unit critical section: [6,10). At t=10 the mutex frees,
H unblocks and, now highest priority again, runs immediately: [10,13).
L runs [0,1), acquires the mutex. M preempts at 1
(nobody is blocked on the lock yet, so no inheritance applies) — runs [1,2). The instant
H blocks at t=2, L inherits H's
priority, which now outranks M — L immediately
preempts M and resumes its critical section: [2,6). At t=6 the mutex frees,
priority reverts, H unblocks and runs: [6,9). M
resumes its remaining 4 units afterward: [9,13).
H arrives at t=2 either way.
Without PIP: starts at t=10, finishes at t=13 (waited 8, response time 11). With PIP:
starts at t=6, finishes at t=9 (waited 4, response time 7). The 4-unit improvement is exactly
M's un-run remainder at the moment H blocked —
PIP didn't make L's critical section shorter, it just stopped an unrelated
task from extending the wait. H's wait under PIP (4) equals exactly
L's full critical-section length — the bounded-inversion guarantee,
independent of how many medium-priority tasks exist.Priority-inversion timeline
interactiveStep through both versions one time unit at a time. Watch H's row (top): under "without PIP" it stays blocked (hollow) far longer, waiting on M's unrelated work; under "with PIP" its wait shrinks to exactly L's remaining critical section.
Pitfalls
- "Priority inversion is a bug someone introduced." It's an emergent property of combining priority scheduling with mutual exclusion — it will happen on any such system without a specific countermeasure, not just poorly-written ones. NASA's Mars Pathfinder hit exactly this in 1997, running well-reviewed code.
- "Priority inheritance eliminates blocking." It bounds it to one critical section's length.
Hstill waited 4 units in the worked example — PIP didn't make that zero, it made it predictable. - "RTOS means the system responds faster." It means the system's worst case is provable. An RTOS is often slower on average than a general-purpose OS doing the same task — the entire trade is average speed for guaranteed bounds.
Practice
- In the worked example, if
Hneeded 5 units of work instead of 3, what would its finish time be under PIP?Show solution
Still starts at t=6 (PIP's bound on the wait doesn't depend on
H's own length), runs 5 units: finishes at t=11. - Variation. Suppose
L's critical section were 2 units instead of 4 (everything else unchanged). RecomputeH's wait under PIP.Show solution
Hblocks at t=2 as before,L(boosted) needs only 2 more units: [2,4).Hstarts at t=4 — wait = 2, still exactly equal toL's critical-section length, confirming the bound scales with the lock-holder's remaining work, not with anything else. - Interpretation. A colleague argues that since PIP bounds the wait to "one critical section,"
the system is now completely immune to missed deadlines. What's missing from that claim?
Show solution
PIP bounds the wait caused specifically by priority inversion. It says nothing about whether that bounded wait is itself short enough to meet
H's actual deadline — a task with a very tight deadline can still miss it even with a perfectly bounded, predictable wait, if that bound is larger than the deadline allows. Bounded and safe are not the same claim; bounded is what makes safety analyzable. - 2.3 noted that RMS's fixed priorities make failure predictable (lowest-priority task misses first
under overload) while EDF's dynamic priorities can miss unpredictably across several tasks at once.
Does priority inversion change that comparison in RMS's favor or EDF's?
Show solution
Priority inversion is orthogonal to the RMS-vs-EDF choice — it is a consequence of mixing any priority-based scheduling (fixed or dynamic) with shared locks, and PIP-style fixes apply to both. It doesn't shift the RMS-vs-EDF trade-off from 2.3; it's a separate axis entirely, which is exactly why a real RTOS needs both a real-time scheduling policy and a priority-inheritance-aware lock implementation — neither alone is sufficient.
Every Decision Is a Negotiation With Scarcity
WriteWell on a desktop assumes gigabytes of RAM and a kernel that can afford to be big. An embedded controller running the same kind of logic might have kilobytes, fixed forever at the moment it was manufactured. Nothing gets to be "good enough for now, upgrade later."
An embedded system is a computer built into a larger device to do one job, with hardware resources — RAM, ROM, CPU, power — chosen at design time to be just enough for that job, not a general-purpose margin for whatever comes next. Every one of this book's earlier trade-offs (kernel architecture, memory allocation, IPC) gets re-decided here under that pressure.
Kernel architecture, revisited under scarcity
1.2 compared monolithic, microkernel, and hybrid designs mainly on isolation and reliability. Add "every kilobyte of RAM and every microsecond of IPC overhead is scarce," and the calculus shifts: a microkernel's message-passing between isolated servers costs cycles and stack space a general-purpose system can absorb without noticing and an embedded one often cannot. Many embedded systems respond by going further toward "minimal" than 1.2's monolithic option even allows — a small, single-image RTOS kernel with no process isolation at all between tasks, or, at the smallest end, a superloop with no kernel or scheduler whatsoever: one big loop polling each device in turn. The trade is 1.2's isolation and fault containment, spent to buy back RAM and determinism.
Task management and scheduling: 9.1, at a smaller scale
Most embedded systems with more than a handful of concurrent responsibilities are running an RTOS in 9.1's sense — the same priority scheduling, the same priority-inversion risk, the same need for bounded interrupt latency. Nothing new to re-derive here; the scheduling theory is 2.3's and 9.1's, applied to a system where a "task" might be a few hundred bytes of stack rather than a full process.
Memory allocation: determinism has a price, and so does its absence
5.2 covered buddy and slab allocators as ways to manage a general-purpose heap efficiently. Embedded systems frequently avoid dynamic heap allocation for RTOS tasks altogether, for the same reason 9.1's kernels avoid demand paging: a heap allocator's search for a free block can take a variable amount of time, and can fail once fragmented — even when the total free memory would seem to be enough.
Worked example 9.2
fragmentation failure vs. static reservationA at [0,200), B at [200,280), C at [280,430). 82 bytes remain free at [430,512).
[200,280) becomes free. Free space now exists in two separate places: 80 bytes at [200,280) and 82 bytes at [430,512). Total free: 162 bytes.
Neither free block (80B, 82B) is large enough on its own, even though their sum (162B) exceeds D's 100B request. The allocation fails.
Heap fragmentation visualizer
interactiveStep through the allocate/free sequence and watch the 512-byte heap fill and fragment. The final step's failed allocation is the whole lesson.
Inter-task communication: lighter than 3.2, and ISR-safe
3.2's monitors and condition variables assume a task can safely block, waiting to be woken later.
That assumption breaks inside an interrupt service routine, which cannot block at all. Embedded systems
typically communicate between tasks — and between an ISR and a task — through
message queues or mailboxes: fixed-size, pre-allocated
buffers with non-blocking try-send/try-receive operations
that an ISR can call safely, alongside blocking variants a normal task can use when it's safe to
wait.
Pitfalls
- "Dynamic allocation is strictly worse here." It's more space-efficient on average (no reserved-but-unused worst-case slack) — it's less predictable, which is the specific thing embedded and real-time systems are willing to pay extra for.
- "Static reservation can't waste memory." It wastes exactly the gap between what's reserved for the worst case and what's actually used at any given moment — the worked example's 530B reservation is memory some of which may sit idle most of the time.
- "Embedded just means small and weak." Embedded hardware spans an enormous range today, some of it quite powerful. The defining trait is the design pressure toward determinism and fixed footprint, which persists even as the absolute numbers grow.
Practice
- Using the worked example's heap after step 2 (B freed), would an allocation request for exactly
82 bytes succeed?
Show solution
Yes — it fits exactly in the free block at [430,512), with 0 bytes to spare.
- Variation. Suppose C were freed instead of B (A and B stay allocated). What are the free
block sizes now, and does a 150-byte request succeed?
Show solution
Free blocks: [280,430) (150B, from freeing C) and [430,512) (82B, unchanged) — these are adjacent and a first-fit allocator that coalesces neighboring free blocks would merge them into a single 232-byte block. A 150-byte request succeeds, unlike the original scenario — because this time the freed block sits right next to the heap's other free space instead of being isolated between two allocated blocks.
- Interpretation. A static pool designer argues "just make every slot big enough for the
largest task, and dynamic fragmentation can never bite you." What's the hidden cost of that fix?
Show solution
Every slot now costs as much memory as the largest task needs, even for tasks that need far less — in the worked example, sizing all 4 slots to A's 200B would cost 800B total for tasks that only actually need 530B combined at their real sizes. Uniform-sized slots trade internal fragmentation (5.2's term: wasted space inside an oversized allocation) for immunity to external fragmentation.
- 9.1 established that RTOS kernels avoid demand paging because page-fault latency is variable.
Explain why a dynamic heap allocator that can fail after fragmenting, as in this section's worked
example, is a similar kind of problem for a hard real-time task — even on a system with no
virtual memory at all.
Show solution
Both are sources of unpredictable failure or delay that a WCET analysis (9.1) cannot bound in advance: a page fault's latency depends on unpredictable disk timing, and a fragmented heap's allocation can outright fail depending on the exact history of prior allocations and frees — neither is a function of the current request alone. A hard real-time task that dynamically allocates memory mid-execution is exposed to exactly the kind of data-dependent, hard-to-bound behavior 9.1's kernel design principles exist to avoid, which is why real-time tasks typically allocate everything they'll ever need once, at startup, and never call a general-purpose allocator again.
Battery Is a Resource the Scheduler Has to Answer To
A desktop running WriteWell never asks whether an autosave is worth the energy. A phone always does — and that one difference reshapes scheduling, memory management, and security into versions of 2, 6, and 7 this book hasn't seen yet.
A mobile OS adds constraints a desktop simply doesn't have: a battery that must last a day, a thermal envelope that throttles performance if exceeded, intermittent connectivity, and a UI that has to feel instantly responsive to touch. Every mechanism below is one of this book's earlier ideas, re-weighed against battery life as a first-class cost, not an afterthought.
Power-aware scheduling: DVFS, and the fight between two good ideas
Dynamic Voltage and Frequency Scaling (DVFS) lets the OS run the CPU at a lower clock frequency and a lower voltage when full speed isn't needed — both save power, and they compound, because a CPU's dynamic power draw scales roughly with voltage squared times frequency. That creates a genuine dilemma the scheduler has to resolve every time it picks a frequency for a given chunk of work: finish fast at high power, then let the CPU sleep (race-to-idle), or spread the same work over more time at low power (graceful slowdown)? Racing to idle wins when the CPU burns a meaningful amount of power just being awake (static or leakage power, paid regardless of clock speed); slowing down wins when that leakage cost is small relative to the power saved by the lower frequency. Neither is the universal right answer — it depends on the hardware's specific power profile.
Worked example 9.3
race-to-idle vs. graceful slowdownHigh: 1,000,000 ÷ 2×109 = 0.5 ms. Low: 1,000,000 ÷ 1×109 = 1.0 ms — half the frequency takes exactly twice as long for the same fixed work.
High: 1.02×2 = 2.0. Low: 0.72×1 = 0.49 — lowering both voltage and frequency together cuts dynamic power by more than 4×.
High: (2.0+0.3)×0.5 = 1.15. Low: (0.49+0.3)×1.0 = 0.79. Low frequency wins — leakage is small enough that the dynamic-power savings dominate.
High: (2.0+1.2)×0.5 = 1.6. Low: (0.49+1.2)×1.0 = 1.69. High frequency wins — leakage is now expensive enough that finishing sooner and going idle beats staying awake twice as long, even at lower power.
DVFS energy comparator
interactiveOpens on the low-leakage worked example. Flip to high leakage and watch which bar — not just its height, but which one is shorter — changes.
Memory management: killing a process instead of paging it
6.2 built page replacement around the assumption that evicting one page and reading it back later is cheap enough to do constantly. On a phone, paging to flash storage costs both battery and flash write endurance (7.5's own "flash" cousin: write cycles are a finite resource). Most mobile OSes respond by skipping page-level eviction almost entirely for background apps and instead killing the entire process when memory is tight — relying on the app being able to save its state and relaunch quickly, rather than 6.2's transparent page-in/page-out. It's a coarser tool than 6.2's, traded for avoiding a genuinely expensive operation on this hardware.
Security and sandboxing: past 7.5's fixed categories
7.5 built POSIX permissions around three fixed categories — owner, group, other — checked in a fixed order. Mobile OSes go further: each app typically runs under its own unique user ID, so there is no meaningful "group" of mutually-trusting apps by default at all, and access to sensitive resources (location, camera, contacts) requires an explicit runtime grant from the person using the device — not just a static bit checked once at open time. This is a different model, not just 7.5's model renamed: POSIX permissions are decided once, by whoever owns the file; mobile permissions can be revoked, per-resource, by the person the device belongs to, at any time.
Pitfalls
- "Lower frequency always saves energy." The worked example's own numbers contradict this — it depends on the leakage-to-dynamic-power ratio, which is a hardware property, not a universal law.
- "Killing background apps is a lazy shortcut." It's a deliberate trade given the real battery and flash-wear cost of paging on this hardware — a coarser tool chosen on purpose, not a missing feature.
- "Mobile sandboxing is just 7.5's permissions with different names." Per-app unique UIDs and runtime-revocable grants are a different model: 7.5's bits are set once by the owner and checked silently; mobile permissions involve an explicit, ongoing decision by the device's user.
Practice
- Using this section's power model, compute the dynamic power at 1.5 GHz and 0.85V.
Show solution
0.852 × 1.5 = 0.7225 × 1.5 = ≈1.084.
- Variation. At leakage = 0.6 (between this section's two scenarios), which frequency wins for
the same 1,000,000-cycle task?
Show solution
High: (2.0+0.6)×0.5 = 1.3. Low: (0.49+0.6)×1.0 = 1.09. Low frequency still wins at this leakage level — the crossover point sits somewhere between 0.6 and 1.2 in this model, not exactly in the middle of the two worked scenarios.
- Interpretation. A phone's battery is at 5%, and the OS aggressively lowers CPU frequency
system-wide. Using this section's model, under what leakage condition could this policy actually
make the battery drain faster in total energy, not slower?
Show solution
When leakage power is high relative to dynamic power — exactly this section's "high leakage" case, where staying awake longer at low frequency costs more total energy than finishing fast and going idle. A low-battery policy that always lowers frequency, without accounting for the chip's leakage profile, can be counterproductive on hardware where race-to-idle is actually the better strategy.
- 6.2 measured page-replacement performance in hit ratio and reuse distance. Explain why "hit ratio"
isn't the right metric for evaluating a mobile OS's decision to kill a background process instead of
paging it.
Show solution
Hit ratio measures how well a retained page cache serves future references — it presumes the data is still around to potentially hit. Killing a process discards its memory entirely; there's no cache to score a hit or miss against afterward. The right metrics here are different: relaunch latency (how long until the app feels usable again) and how much in-progress state was lost — 6.2's question ("how much do we save by keeping this page?") isn't the question this decision is even answering.
Cheat Sheet
Everything computed in this chapter, in one place.
9.1 · RTOS
RMS/EDF bounds are 2.3's; this chapter adds the kernel that makes them hold
priority inversion: emergent from priority scheduling + mutexes, not a bug
PIP bounds the wait to exactly one lower-priority critical section (verified: 8→4 units)
9.2 · Embedded
512B heap, first-fit: 162B free, 100B request, fails — fragmented into 80B+82B
static reservation for the same 4 tasks needs 530B — more than the dynamic budget
message queues/mailboxes: ISR-safe where 3.2's monitors are not
9.3 · Mobile
DVFS: low leakage (0.3) favors low frequency (0.79 vs 1.15); high leakage (1.2) favors racing to finish (1.6 vs 1.69)
kills whole processes instead of 6.2-style paging — avoids flash write-endurance cost
per-app UID + runtime-revocable grants — not just 7.5's bits with new names
Mixed Self-Test
Not grouped by section — figure out which idea applies before you answer, the way an exam will make you.
- A low-priority task holds a lock a high-priority task needs. No other task exists in the system.
Does priority inversion, in the sense this chapter defines it, occur?
Show solution
No — the high-priority task simply waits for the lock to be released, which is ordinary, bounded blocking. Priority inversion specifically requires a third, unrelated medium-priority task to extend that wait indefinitely. Two tasks and a lock is just synchronization (3.2); three tasks and a lock is where 9.1's problem appears. (9.1)
- A 256-byte heap has two free blocks of 60 and 70 bytes (130 total) after some allocations and frees.
Does a 65-byte request succeed?
Show solution
Yes — 65 fits inside the 70-byte block on its own; no fragmentation problem arises here because at least one single free block is already big enough. (9.2)
- Using 9.3's power model, which uses more total energy for the same fixed task: running longer at
low leakage, or running longer at high leakage?
Show solution
High leakage — leakage power is paid for every unit of time the CPU stays awake, so a policy that runs longer (low frequency) pays that cost proportionally more when leakage is high, which is exactly why high leakage flips the winner toward finishing fast instead. (9.3)
- True or false: an RTOS is, by definition, faster than a general-purpose OS running the same
workload.
Show solution
False. An RTOS optimizes for provable, bounded worst-case response time, often at the cost of average-case speed (giving up 6.2's demand paging, for instance). It is entirely possible for a general-purpose OS to be faster on average while being unable to guarantee any particular deadline. (9.1)
- A static memory pool reserves 800 bytes total for four possible tasks that, in practice, are never
all active simultaneously — at most two run at once, needing at most 350 bytes together. What
is this design paying for, and is it getting good value for it?
Show solution
It is paying for determinism — the guarantee that an allocation request from any of the four tasks will always succeed, instantly, regardless of history. Whether that's good value depends entirely on whether the system actually needs that guarantee (9.1's hard-real-time case) or would be fine with a smaller, occasionally-fragmenting dynamic heap (9.2) — there's no universal answer, only a stated requirement to design against. (9.2)
- A phone's OS is under memory pressure and must free up RAM. Contrast what 6.2's desktop-style
virtual memory system would do with what 9.3 says a typical mobile OS does instead, and name one
concrete cost the mobile approach is avoiding.
Show solution
6.2's approach: evict individual cold pages to a swap area on disk (or in this case, flash), keeping every process nominally alive. 9.3's mobile approach: kill entire low-priority background processes outright, relying on fast relaunch rather than transparent restoration. The concrete cost avoided: flash storage's finite write-endurance (7.5) and the battery cost of the write itself — both of which 6.2's constant paging would spend freely on a desktop with a traditional disk. (6.2, 9.3)
- 7.2's ordered journaling mode and 9.1's priority inheritance protocol both exist to bound an
otherwise-unpredictable delay. What delay does each one bound, and are they solving the same
underlying problem?
Show solution
Different problems, same shape of fix. 7.2's ordered mode bounds which crash-time states are possible (data must precede its metadata commit) — a correctness/durability guarantee. 9.1's PIP bounds how long a high-priority task can be delayed by a lower-priority one — a timing/schedulability guarantee. Both work by constraining an otherwise-free-floating ordering (of writes, or of task execution) to make the system's worst case provable instead of merely likely. (7.2, 9.1)
- Across Chapters 7, 8, and 9, WriteWell's freemap mutex has appeared in a crash-consistency
transaction (7.2/7.3), a disk-scheduling request (8.3), and a priority-inversion scenario (9.1). What
does its recurring presence across three very different problems suggest about how many genuinely
separate concerns a single shared resource can be entangled in?
Show solution
At least three, simultaneously: durability (does the freemap update survive a crash correctly ordered relative to the data it describes — 7.2), throughput (how quickly the disk can actually service the write once scheduled — 8.3), and timing predictability (how long a higher-priority task can be blocked waiting for the same lock — 9.1). None of these concerns implies or subsumes the others — a system can solve any one while remaining vulnerable in the other two, which is exactly why real operating systems need dedicated mechanisms for each rather than expecting one fix to cover all three. (7.2, 7.3, 8.3, 9.1)
Further reading
- Stallings, Operating Systems: Internals and Design Principles, 9th ed., and Silberschatz, Galvin & Gagne, Operating System Concepts, 10th ed., Ch. 5 (CPU Scheduling). Both texts cover real-time scheduling as an extension of their main scheduling chapter, the same organization 2.3 and 9.1 follow here.
- Sha, Rajkumar & Lehoczky, "Priority Inheritance Protocols: An Approach to Real-Time Synchronization," IEEE Transactions on Computers, 1990. The original paper behind 9.1's priority inheritance protocol and the stronger priority ceiling protocol; proves the one-critical-section blocking bound used in this chapter's worked example.
- Liu & Layland, "Scheduling Algorithms for Multiprogramming in a Hard-Real-Time Environment," Journal of the ACM, 1973. Already cited in 2.3 for RMS/EDF; the same paper underlies why 9.1 treats bounded blocking as something that composes cleanly with those schedulability results.
- Embedded and mobile OS design is documented far more in platform-specific sources (RTOS vendor manuals, Android's and iOS's own engineering documentation) than in a single unified textbook chapter — 9.2 and 9.3's mechanisms (static pools, message queues, DVFS, per-app sandboxing) are industry-standard patterns rather than one paper's contribution, which is itself worth knowing going in.
Before this chapter — nearly all of it. 9.1 builds on 2.3's scheduling and 3.2's mutexes; 9.2 builds on 1.2's kernel architectures and 5.2's allocators; 9.3 builds on 6.2's page replacement and 7.5's permission model. This chapter introduced almost no new primitive — it re-examined the book's existing ones under constraints a general-purpose desktop never faces.
Closing note. Nine chapters ago, WriteWell was a text editor with two threads and a document to save. It never changed. What changed, chapter by chapter, was everything underneath it: how its threads were scheduled and synchronized, how its memory was paged, how its file survived a crash, how its disk requests were ordered, and now, how all of that holds up when the machine has to be provably on time, physically tiny, or running on a battery. An operating system is not one idea; it's this whole stack of trade-offs, each one a deliberate answer to a constraint the hardware or the workload imposes — and every constraint in this book was, in the end, just a different way of asking the same question WriteWell asked in Chapter 1: what does the OS owe the program that trusts it to keep running?