Capstone: A Small Multitasking OS Running Real Concurrent Programs

Building an Operating System Kernel: Concurrency, I/O & Synchronization

Chapter 10 · Capstone: A Small Multitasking OS Running Real Concurrent Programs

Nine chapters, each independently verified — a mutex, a semaphore, a message queue, deadlock detection, a disk driver, interrupt-driven I/O, a VFS, a syscall library. This capstone wires all of them together with Course 1's own priority scheduler for the first time — and finds the exact same class of bug oskernel1's own capstone found, in the exact same place.

Step 1: Memory, Processes, and a Real Filesystem — Assembled

Verified directly — real physical memory, a real Kernel, a real VFS with mounted files
Real physical memory, a real Kernel, and a real VFS with two mounted files, assembled from every chapter's own unmodified classes — the foundation the rest of this capstone builds on.

Step 2: The Same Bug, Again — Priority Scheduling Meets Blocking I/O

Chapter 6/7's own IOKernel was built and tested against Course 1's plain Scheduler. Course 1 Chapter 9's own AgingScheduler returns a (pcb, priority) tuple from pick_next(), not a bare PCB — the identical shape of mismatch oskernel1's own capstone already found once, between two different chapters.

Verified directly — raises: 'list' object has no attribute 'registers'
Combining Chapter 6/7's own blocking-I/O kernel with the priority-and-aging scheduler from Course 1, unmodified, crashes the instant a process tries to resume after blocking. Both schedulers were independently correct against their own tests. Neither was ever designed against a shared interface — and this is the second time in this project's own history that exact failure mode has appeared.

Step 3: The Fix — A Unified Kernel

class UnifiedKernel: def _on_timer(self): self.ticks_since_switch = 0 self.scheduler.age_waiting() # Chapter 9's own fix picked = self.scheduler.pick_next() if picked is None: return next_pcb, _priority = picked # unpack correctly context_switch_fixed(self.cpu, self.current_pcb, next_pcb) if self.current_pcb is not None: self.scheduler.add(self.current_pcb, self.base_priorities[self.current_pcb.pid]) self.current_pcb = next_pcb
Verified directly — blocking I/O and priority scheduling correctly combine
With tuple-unpacking, age_waiting(), and base-priority-reset applied everywhere the scheduler is touched — including _on_disk_complete() and _switch_away_from_blocked() — a process blocks on real disk I/O, a lower-priority process gets real turns during the wait, and both complete cleanly with no crash.

Step 4: A Full, Real Multi-Process Scenario

Three real processes — one blocking on real disk I/O, two competing on priority — all incrementing a single real, mutex-protected shared counter through real syscalls.

Verified directly — 45 of 45 real increments land correctly, zero lost
Target: 45 (15 increments × 3 processes). Actual final counter value: 45, in 53 real steps. Every increment — issued by a different process, through a real syscall trap, under real preemptive priority-and-aging scheduling, with one process genuinely blocking on real disk I/O midway through the run — lands correctly. Chapter 1's own lost-update race: resolved. This capstone's own Step 2 bug: fixed. The mutex genuinely serializes the shared counter across all three processes, correctly, every time.

Chapter Attribution

Capstone componentBuilt in
Physical memory, real processesCourse 1, Chapters 2 & 4
Context switching, interruptsCourse 1, Chapters 5 & 6
Priority scheduling with agingCourse 1, Chapter 9
The atomic mutexCourse 2, Chapter 2
Real, mutex-protected shared stateCourse 2, Chapters 1 & 2 (resolving Chapter 1's own race directly)
Real disk driver and blocking I/OCourse 2, Chapters 6 & 7
The VFSCourse 2, Chapter 8
The real syscall trapCourse 2, Chapter 9
The pick_next() interface mismatch, found and fixedThis capstone — the same class of bug oskernel1's own capstone found, recurring at a new seam

What This Course Doesn't Cover

This kernel remains a real, verified Python simulation of genuine kernel mechanisms — not bare-metal code, not assembly, nothing that runs on real hardware. Deliberately out of scope across both courses: multi-core/SMP scheduling, real device drivers for actual hardware, a networking stack, and journaling or crash-consistent file systems (Building a Database Engine's own write-ahead logging covers that territory in depth, for a different kind of storage system).

The project, closed
Two courses, twenty chapters: physical and virtual memory, a real process state machine, context switching, a syscall boundary with quota enforcement, cooperative and preemptive scheduling, priority with aging — then real synchronization primitives, deadlock detection and recovery, inter-process communication, a real device driver with interrupt-driven I/O, a virtual file system, and a real syscall library tying it all together. Two genuine cross-chapter integration bugs found and fixed along the way, both times by combining independently-correct pieces for the first time — the same lesson, learned twice, in two different courses.

Capstone Quick Reference

  • Step 1: memory + processes + VFS, assembled from every chapter's own unmodified classes
  • Step 2 (bug): Course 1's priority scheduler and Course 2's blocking-I/O kernel disagree about what pick_next() returns — the same class of bug as oskernel1's own capstone
  • Step 3 (fix): a unified kernel applying tuple-unpacking, age_waiting(), and base-priority-reset at every point the scheduler is touched
  • Step 4: 3 real processes, 45 real mutex-protected syscalls, real disk blocking, real priority scheduling — 45 of 45 correct
  • The one big lesson, twice over: independently-correct components can still disagree the moment they're actually combined — only real, end-to-end integration finds the gap
  • Course complete: Building an Operating System Kernel: Concurrency, I/O & Synchronization, 10/10 chapters — closing the full 20-chapter, two-course project