This is inspired by teachyourselfcs. OSTEP is one of those books that explains complex stuff in a simple manner.
What are these chapters about?
Ch 1â5 are basically the OS âorigin storyâ:
- why OS exists at all
- what a process really is
- how the OS creates/runs programs (
fork/exec/wait) - how we fake âmany CPUsâ on one CPU (scheduling / time-sharing)
- why the OS draws a hard line between safe and powerful (user vs kernel)
The crux
OS is a resource manager. It virtualize the hardware (CPU/mem/disk) without letting programs wreck each other or the system.
Key definitions to rememeber
- Abstraction: a nicer interface over ugly reality; fundamental idea to CS; makes system convenient and easy to use.
- Virtualization: takes a physical resource (CPU, memory, and disk etc.) & converts them into a virtual form.
- Process: a running program + the machine state needed to keep it running.
- Machine state (what makes a process):
- Memory / address space: processâs private stuff -> code + data it can access
- Registers: CPU state the program is using
- Special registers:
- Program Counter/IP: where we are in the code (what runs next)
- Stack Pointer/FP: stack management; where we are on the stack (function calls, locals, returns)
- I/O state: what it has open / connected (files, stdin/stdout, etc.)
- File system: OS layer that manages persistent storage (disk/SSD) and controls access.
Key insights
- CPU, memory, disk are the real physical resources. OS makes them feel virtual so multiple programs can coexist.
- Each process gets its own âfakeâ private memory (virtual address space). Two processes can both use the same address and itâs fine- OS maps them to different physical memory behind the scenes.
- We get âmany CPUsâ via time-sharing: run process A for a slice, then switch to B, etc. (and itâs fast enough that it looks parallel).
- The OS has to track a bunch of bookkeeping to pull this off:
- a process list / task list
- one record per process: PCB (process control block)
- PCB matters because it stores enough info to stop/resume a process:
- saved register context (used for context switches)
- process state (not just running/ready/blocked)
- There are extra states that matter:
- embryo (being created)
- sleeping (blocked)
- runnable
- running
- zombie (dead but not cleaned up yet)
- Zombie state exists so the parent can still read the childâs exit code.
wait()is basically the parent saying âok I got it, you can clean up nowâ.
Mechanisms / APIs (the concrete stuff)
System calls (user mode â kernel mode bridge)
Apps shouldnât be able to read any bytes from disk whenever they want (privacy/security would collapse). So the file system isnât just a normal library you link against- access has to be mediated by the OS via system calls.
- Goal: let programs do privileged things (files, I/O, memory stuff) without giving them god-mode.
- How it works: A system call is the controlled bridge between the two: it transfers control into the OS and temporarily (trap handler) raises the hardware privilege level only for that OS code, with checks/permissions enforced along the way.
- Gotcha: normal function calls donât change privilege. Syscalls do.
Program -> Process (loading + starting)
- OS loads the program from disk into memory (code + static data).
- sets up the stack (and fills
main(argc, argv)). - sets up a small heap (grows later when you
malloc()). - sets up basic I/O (stdin/stdout/stderr).
- then jumps to
main()and youâre ârunningâ.
Eager vs lazy loading:
- old/simple OS: load everything upfront
- modern OS: load on demand (paging/swapping magic later)
fork() (make a child)
- child is basically a copy (its own memory, registers, PC).
- but the return value differs:
- parent gets child PID
- child gets
0(so both can tell who they are)
exec() (run a new program)
- replaces the current process image with a new executable.
- after successful
exec(), the old code doesnât keep going.
wait() (cleanup + exit code)
- parent waits for child to finish
- grabs exit status
- tells OS it can clean up (prevents zombies piling up)
Why fork() + exec() is split (shell reason)
This is the main motivation:
- shell does
fork() - child does some setup (redirection/pipes/env)
- then
exec()to actually run the command - parent
wait()s
Without that gap between fork and exec, shells would be way harder.
Common pitfalls
- thinking âprocess = programâ (itâs program + state)
- assuming virtual addresses are globally real (theyâre per-process)
- assuming the parent/child run order after
fork()(nope) - forgetting
wait()and leaving zombies around - forgetting that syscalls are special (privilege boundary)
TL;DR
- OS virtualizes hardware resources so multiple programs can run safely.
- A process = program + machine state (memory, regs, I/O).
- Syscalls are the controlled way to do privileged operations.
fork/exec/waitexplains how UNIX process management (and shells) work.- Scheduling/time-sharing is how we fake âmany CPUsâ.