PintOS: Deliberate Complexity
The second week of PintOS covered user programs. The flow grew more complex than the Threads part, and understanding the structure of PintOS piece by piece took time.
The hardware amounts to only a handful of devices how can it get this complicated? The cause was the abstractions the OS had built. Before chasing down what a process is, what an fd is, what wait is, I wanted to ask first why the OS needs such abstractions at all.

That is why PintOS came to look like a small empire. The kernel is the emperor, holding every resource directly; user programs are its subjects, moving only within limited authority.
System calls are the checkpoints between them, and an fd looked like a permit for reaching the kernel's objects. What matters is not the metaphor itself. I wanted to understand first why PintOS chose the abstractions it has and then doubt them, at least once.
The OS appears to hand users freedom, but what it actually provides is dangerous freedom converted into a safe form. It does not hand over the file; it hands over an fd. It does not let address spaces be shared directly; it isolates them as processes. It does not let a process simply vanish at the end; it reclaims the ending through wait and exit.
The first thing open to doubt was the fd. In PintOS, an fd is basically an integer. The implementation is simple and easy to understand. But thinking of real systems raises a question: is a plain number enough?
An fd that can only read, an fd that can only write, an fd that must close after exec if permission and lifetime could be expressed together, a safer abstraction becomes possible. The fd then stops being a mere index and becomes a capability, and read and write stop being code that merely calls file functions they become code that checks whether the current process has the right to do that work.
The second was fork. PintOS's fork copies the parent's address space. As a teaching implementation, it is clear: it shows most directly that parent and child hold independent flows of execution. But for a child about to call exec, most of the freshly copied address space may simply be thrown away.
The need for copy-on-write, the choice other OSes made, becomes clear on its own. Not copying everything up front; sharing first, and splitting only when a write actually occurs can be the better choice. What matters in fork is not the "copy." It is when, and at what cost, independence is guaranteed.
The third was wait and exit. In PintOS, a process ending is not simply execution stopping. An exit status must be left behind; the parent must collect it exactly once; and the cases where the parent dies first, or the child does, must be cleaned up as well. wait and exit feel like a lifetime-management protocol.
Tying the child's exit information to the parent's list is simple, but when the two lifecycles cross, responsibility can blur. Beyond states like parent_alive and child_alive in the child info, a reference count is also worth considering to state clearly who must be the last to release that information.
Understanding PintOS's abstractions and doubting them left room for interesting questions and possible answers. I used to think abstraction was a means of hiding complexity.
The abstraction I saw in PintOS is, rather, deliberate complexity a way of deciding where to place permission, cost, and lifetime. The fd is a permission abstraction that keeps kernel objects from being exposed directly. Fork is a cost model for providing independence. Wait and Exit are a lifetime abstraction for reclaiming a process's final responsibility.
PintOS shows these choices in a deliberately simple form, and it was a good place to ask why an OS needs such abstractions.