Exercise 2: A Failed Process Creation Leaks No Frames — Possible Solution ==================================================================== THE TEST ------------------------------ mem = PhysicalMemoryFramed(num_frames=5) kernel = Kernel(mem) before = len(mem.free_frames) too_big = kernel.create_process(num_pages=10) # far more than 5 frames exist after = len(mem.free_frames) still_works = kernel.create_process(num_pages=5) # a genuinely fitting request RESULT ------------------------------ requesting a 10-page process from a 5-frame memory returns: None free frames before the failed attempt: 5, after: 5 a genuinely fitting 5-page request afterward: PID 18 too_big is None, free frame count is completely unchanged by the failed attempt (5 before, 5 after), and a request that actually fits succeeds cleanly right afterward. WHY THE FAILED ATTEMPT DOESN'T LEAK ANYTHING ------------------------------ create_process()'s own loop allocates frames one at a time, tracking everything it's allocated so far in a local owned_frames list: for vpn in range(num_pages): pfn = self.mem.alloc_frame() if pfn is None: for f in owned_frames: # roll back everything allocated so far self.mem.free_frame(f) return None page_table.map_page(vpn, pfn) owned_frames.append(pfn) For a 10-page request against 5 frames, the loop successfully allocates all 5 real frames (appending each to owned_frames), then on the 6th iteration alloc_frame() returns None (the pool is now empty). At that exact point, the rollback loop immediately frees all 5 frames this attempt had already claimed, before the function returns None to the caller. No PCB is ever created for a failed attempt, and no frame is left allocated to nothing. WHY THIS MATTERS AS A DISTINCT FINDING FROM THE CHAPTER'S OWN BUG ------------------------------ Finding 2's own bug was about a process that DID succeed, then leaked its resources on exit. This exercise checks the opposite moment: a process that never successfully starts at all. Both are real, plausible ways for a kernel to leak memory, and they require genuinely different code to get right -- rollback-on-partial-failure during creation, versus reclaim-on-exit during termination. Verifying both independently confirms neither path was overlooked. WHY THIS WORKS AS AN ANSWER ------------------------------ Measuring the exact free-frame count immediately before and after the failed attempt -- rather than just checking that the function returned None -- proves the rollback is complete and exact, not just "probably fine," and confirming a subsequent legitimate request still succeeds rules out any subtler form of corruption the failed attempt might otherwise have left behind.