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.

Colour contract Linux — primary OS Windows — secondary OS process-level — a whole task, allocated block, or the resource currently active/held thread-level — a request issued by one of WriteWell's own threads (T_ui or T_save)

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.

9.1 · RTOS: KERNEL DESIGN, SCHEDULING & PRIORITY INVERSION

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 PIP
Three tasks share a CPU. L (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.
Without priority inheritance

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).

With priority inheritance

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 ML 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).

AnswerH 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
interactive
t = 0
 

Step 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. H still 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

  1. In the worked example, if H needed 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.

  2. Variation. Suppose L's critical section were 2 units instead of 4 (everything else unchanged). Recompute H's wait under PIP.
    Show solution

    H blocks at t=2 as before, L (boosted) needs only 2 more units: [2,4). H starts at t=4 — wait = 2, still exactly equal to L's critical-section length, confirming the bound scales with the lock-holder's remaining work, not with anything else.

  3. 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.

  4. 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.

9.2 · EMBEDDED OS: KERNEL ARCHITECTURE, TASK MANAGEMENT & RESOURCE CONSTRAINTS

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 reservation
A 512-byte heap, first-fit allocation. Allocate A (200B), B (80B), C (150B) in that order, then free B, then try to allocate D (100B).
Step 1 · allocate A, B, C

A at [0,200), B at [200,280), C at [280,430). 82 bytes remain free at [430,512).

Step 2 · free B

[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.

Step 3 · allocate D (100B)

Neither free block (80B, 82B) is large enough on its own, even though their sum (162B) exceeds D's 100B request. The allocation fails.

Answer — classic external fragmentation: 162 bytes free, a 100-byte request, and a failure anyway, because no single contiguous piece is big enough. A static alternative — reserving A, B, C, and D's worst-case sizes (200+80+150+100 = 530 bytes) up front, fixed at compile time — would never hit this failure mode at all, but needs more total memory (530B) than this heap's own budget (512B) provides, and commits it whether or not all four tasks are ever actually active at once. Neither approach is strictly better; they trade a rare, data-dependent failure for a larger, guaranteed-up-front commitment.
Heap fragmentation visualizer
interactive
step 0 of 5
 

Step 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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

9.3 · MOBILE OS: POWER-AWARE SCHEDULING, MEMORY & SECURITY

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 slowdown
A fixed 1,000,000-cycle task (say, WriteWell's background spell-check pass) can run at 2 GHz/1.0V or 1 GHz/0.7V. Dynamic power ≈ V2×f (relative units). Compare total energy under two static-leakage assumptions: low (0.3) and high (1.2).
Step 1 · time at each frequency

High: 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.

Step 2 · dynamic power at each point

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×.

Step 3 · total energy, low leakage (0.3)

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.

Step 4 · total energy, high leakage (1.2)

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.

Answer — the winner flips depending on one hardware property (leakage) the OS doesn't control and typically can't change at runtime. There is no universally correct DVFS policy; there's only a policy that's correct for a given chip's power profile, which is why real mobile schedulers use empirically-tuned frequency tables rather than one fixed rule.
DVFS energy comparator
interactive
 

Opens 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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

▣ · REVISION

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

▣ · SELF-TEST

Mixed Self-Test

Not grouped by section — figure out which idea applies before you answer, the way an exam will make you.

  1. 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)

  2. 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)

  3. 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)

  4. 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)

  5. 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)

  6. 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. 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)

  8. 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?