← Back to all projects

OPERATING SYSTEMS FROM FIRST PRINCIPLES

Operating Systems from First Principles

Goal: Understand how operating systems work at the deepest level—from bare metal to user space—and grasp why Unix’s design philosophy has proven so powerful and enduring.

This learning path doesn’t just teach you OS concepts; it helps you understand why operating systems are designed the way they are, why Unix “won,” and what makes its abstractions so elegant that they’ve survived for 50+ years.


The Big Questions We’ll Answer

  1. What is an operating system, really? (It’s smaller than you think)
  2. Why do we need one? (Hardware is chaos; OS brings order)
  3. What are the core abstractions? (Processes, files, memory—that’s almost it)
  4. Why is Unix so elegant? (Everything is a file, small tools, composition)
  5. How is Windows different? (Different trade-offs, different philosophy)
  6. Why did Unix “win”? (Simplicity scales; complexity doesn’t)

The Unix Philosophy: Why It Matters

Before diving into projects, understand this: Unix isn’t just an operating system—it’s a philosophy about how to build complex systems from simple parts.

The Core Principles

1. Everything is a file
   - Keyboard? File. Screen? File. Network socket? File. Process info? File.
   - One interface (read/write) to rule them all
   - Radical simplification that enables composition

2. Small, sharp tools that do one thing well
   - `cat` just concatenates. `grep` just searches. `sort` just sorts.
   - Combine them: `cat log.txt | grep ERROR | sort | uniq -c`
   - Emergent complexity from simple parts

3. Text streams as universal interface
   - Programs don't need to understand each other's internals
   - Just read lines, process, write lines
   - Pipelines become possible

4. Composition over monoliths
   - Don't build one program that does everything
   - Build many programs that work together
   - The shell is the glue

Why This Won

Windows philosophy:     "Build complete applications with rich APIs"
Unix philosophy:        "Build simple tools that compose"

Windows result:         Complex, tightly coupled, hard to automate
Unix result:            Simple, loosely coupled, infinitely scriptable

When the internet arrived:
  - Unix tools composed into web servers, databases, automation
  - The "everything is a file" model extended naturally to networks
  - Small tools meant small attack surface, easier security

When cloud computing arrived:
  - Unix's text-based configuration meant easy automation
  - Containers are just Unix namespaces + cgroups
  - Kubernetes runs on Unix philosophy

Unix won because simplicity scales. Complexity doesn't.

The Core OS Abstractions (There Are Only Three!)

Everything in an operating system reduces to managing three things:

┌─────────────────────────────────────────────────────────────────┐
│                        USER PROGRAMS                            │
├─────────────────────────────────────────────────────────────────┤
│                     SYSTEM CALL INTERFACE                       │
├───────────────────┬───────────────────┬─────────────────────────┤
│     PROCESSES     │      MEMORY       │      FILES/IO           │
│                   │                   │                         │
│  - Creation       │  - Allocation     │  - Everything is a file │
│  - Scheduling     │  - Virtual memory │  - Read/Write/Open/Close│
│  - Communication  │  - Protection     │  - Directories          │
│  - Termination    │  - Sharing        │  - Devices              │
├───────────────────┴───────────────────┴─────────────────────────┤
│                         HARDWARE                                │
│              CPU         Memory         Disk/Devices            │
└─────────────────────────────────────────────────────────────────┘

That’s it. Processes, memory, files. Everything else is implementation detail.


Windows vs Unix: Architectural Differences

                    UNIX                           WINDOWS
                    ────                           ───────
Kernel Model:       Monolithic                     Hybrid (NT kernel)
                    (everything in kernel)         (some services in user space)

File System:        Hierarchical, single root      Drive letters (C:, D:)
                    /home/user/docs                C:\Users\User\Documents

Everything File:    YES - devices are files        NO - devices are objects
                    /dev/sda, /dev/tty             \\.\PhysicalDrive0

Text Config:        /etc/passwd, ~/.bashrc         Registry (binary database)
                    Human readable, scriptable     GUI-oriented, harder to script

Process Model:      fork() + exec()                CreateProcess()
                    Clone parent, then transform   Create fresh process

IPC:                Pipes, sockets, signals        Named pipes, COM, RPC, WMI
                    Simple, file-like              Rich, complex APIs

Permissions:        User/Group/Other (rwx)         ACLs (Access Control Lists)
                    Simple, coarse                 Fine-grained, complex

Shell:              First-class citizen            Afterthought (until PowerShell)
                    sh/bash/zsh                    cmd.exe was primitive

Philosophy:         "Worse is better"              "The right thing"
                    Simple beats complete          Complete beats simple

Why These Differences Matter

Windows chose richness: Complex APIs, GUI-first, binary formats. Great for desktop applications with rich UIs. Harder to automate, script, compose.

Unix chose simplicity: Text formats, CLI-first, everything is a file. Awkward for desktop GUIs. Trivial to automate, script, compose.

The internet, servers, and cloud rewarded Unix’s choices. Automation won.


Part 1: Understanding the Hardware-Software Boundary

Before you can understand an OS, you must understand what the hardware provides and what it doesn’t.


Project 1: Bare Metal “Hello World” (No OS)

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: Assembly (x86 NASM)
  • Alternative Programming Languages: x86 GAS syntax, ARM Assembly
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: Boot Process / Hardware Interface
  • Software or Tool: QEMU Emulator
  • Main Book: “Operating Systems: Three Easy Pieces” by Arpaci-Dusseau

What you’ll build: A 512-byte boot sector that, when loaded by the BIOS, prints “Hello, OS!” directly to the screen—with no operating system, no libraries, no runtime. Just your code and the hardware.

Why it teaches OS fundamentals: This strips away every abstraction. You’ll understand that the CPU is just a machine that fetches instructions and executes them. The BIOS loads your code to a specific memory address (0x7C00), jumps to it, and you’re on your own. No printf, no syscalls—just raw hardware access.

Core challenges you’ll face:

  • Understanding the boot process → maps to how computers start
  • Writing to video memory directly → maps to memory-mapped I/O
  • No standard library, no OS services → maps to what the OS actually provides
  • Real mode vs protected mode → maps to CPU privilege levels
  • The 512-byte boot sector limit → maps to bootstrapping constraints

Key Concepts:

  • Boot Process: “Operating Systems: Three Easy Pieces” Chapter 36 - Arpaci-Dusseau
  • x86 Real Mode: “Write Great Code, Volume 2” Chapter 3 - Randall Hyde
  • BIOS Interrupts: “The Art of Assembly Language” Chapter 13 - Randall Hyde
  • Memory-Mapped I/O: “Computer Systems: A Programmer’s Perspective” Chapter 6 - Bryant & O’Hallaron

Difficulty: Expert Time estimate: Weekend (intense) Prerequisites: Basic assembly understanding, willingness to read documentation

Real world outcome:

$ nasm -f bin boot.asm -o boot.bin
$ qemu-system-x86_64 -drive format=raw,file=boot.bin

[QEMU window opens]
[Black screen with white text:]
Hello, OS!
_

[Cursor blinking. That's YOUR code running on bare metal!]

Implementation Hints: The boot sector must:

  1. Be exactly 512 bytes
  2. End with magic bytes 0x55 0xAA (BIOS signature)
  3. Start at 0x7C00 in memory (BIOS loads it there)

Video memory in text mode starts at 0xB8000. Each character is 2 bytes: ASCII code + attribute (color).

; Pseudo-structure (not actual code!)
; Write 'H' in white on black at position 0
mov word [0xB8000], 0x0F48  ; 0x0F = white on black, 0x48 = 'H'

BIOS interrupt int 0x10 can also print characters, but writing to video memory directly shows you what “no OS” really means.

Learning milestones:

  1. Boot sector loads and executes → You understand the boot process
  2. Text appears on screen → You understand memory-mapped I/O
  3. You realize there’s nothing else → You understand what an OS must provide

Project 2: Bootloader that Loads a Kernel

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: Assembly + C
  • Alternative Programming Languages: Assembly + Rust, Pure Assembly
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: Boot Process / Protected Mode
  • Software or Tool: Custom Bootloader
  • Main Book: “Operating Systems: Three Easy Pieces” by Arpaci-Dusseau

What you’ll build: A bootloader that loads a larger kernel from disk into memory, switches the CPU from 16-bit real mode to 32-bit protected mode, and jumps to the kernel. This is what GRUB does, but you’ll build it yourself.

Why it teaches OS fundamentals: The 512-byte boot sector limitation forces a two-stage boot. This teaches disk I/O (reading sectors), memory layout, CPU mode switching, and the hand-off from firmware to OS. You’ll understand why bootloaders exist.

Core challenges you’ll face:

  • Reading from disk using BIOS → maps to block device I/O
  • Setting up the Global Descriptor Table (GDT) → maps to memory segmentation
  • Switching to 32-bit protected mode → maps to privilege and protection
  • Jumping from 16-bit to 32-bit code → maps to ABI transitions
  • Loading kernel to correct memory address → maps to memory layout planning

Key Concepts:

  • Protected Mode: “The Art of Assembly Language” Chapter 8 - Randall Hyde
  • GDT and Segmentation: “Operating Systems: Three Easy Pieces” Chapter 15 - Arpaci-Dusseau
  • Disk I/O: “Linux Kernel Development” Chapter 13 - Robert Love
  • Two-Stage Boot: “Operating Systems Design and Implementation” Chapter 2 - Tanenbaum

Difficulty: Expert Time estimate: 1-2 weeks Prerequisites: Project 1, understanding of x86 architecture

Real world outcome:

$ make
nasm -f bin boot.asm -o boot.bin      # Stage 1 bootloader
nasm -f elf32 kernel_entry.asm -o kernel_entry.o
gcc -m32 -ffreestanding -c kernel.c -o kernel.o
ld -m elf_i386 -T linker.ld -o kernel.bin kernel_entry.o kernel.o
cat boot.bin kernel.bin > os.bin

$ qemu-system-x86_64 -drive format=raw,file=os.bin

[Screen shows:]
Bootloader: Loading kernel from disk...
Bootloader: Switching to protected mode...
Bootloader: Jumping to kernel...

=== MyOS Kernel v0.1 ===
Running in 32-bit protected mode
Kernel loaded at 0x1000
Hello from C!

Implementation Hints: Stage 1 (512 bytes): Use BIOS int 0x13 to read sectors from disk. Load stage 2 / kernel to a known address (e.g., 0x1000).

Protected mode switch requires:

  1. Disable interrupts (cli)
  2. Load GDT (lgdt)
  3. Set PE bit in CR0
  4. Far jump to flush pipeline and load CS with protected mode selector

The GDT defines memory segments. For a flat memory model (which you want), set base=0, limit=4GB for both code and data segments.

Learning milestones:

  1. Kernel loads from disk → You understand block device I/O
  2. Protected mode switch works → You understand CPU privilege levels
  3. C code runs in your kernel → You understand the runtime environment

Project 3: Memory Allocator (malloc from Scratch)

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust, C++
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 3: Advanced (The Engineer)
  • Knowledge Area: Memory Management / Data Structures
  • Software or Tool: malloc Implementation
  • Main Book: “The Linux Programming Interface” by Michael Kerrisk

What you’ll build: Your own implementation of malloc(), free(), and realloc() that manages a heap, handles fragmentation, and coalesces free blocks. Then use it in real programs.

Why it teaches OS fundamentals: Memory allocation is the heart of the memory management abstraction. Understanding how the heap works—block headers, free lists, splitting, coalescing—reveals what the OS does for every program. You’ll also understand why memory bugs (use-after-free, double-free) are dangerous.

Core challenges you’ll face:

  • Requesting memory from OS → maps to sbrk() or mmap()
  • Block headers and metadata → maps to memory layout
  • Free list management → maps to data structures in constrained memory
  • Splitting and coalescing → maps to fragmentation prevention
  • Alignment requirements → maps to hardware constraints

Key Concepts:

  • Dynamic Memory Allocation: “Computer Systems: A Programmer’s Perspective” Chapter 9 - Bryant & O’Hallaron
  • sbrk and mmap: “The Linux Programming Interface” Chapter 7 - Michael Kerrisk
  • Allocator Strategies: “Operating Systems: Three Easy Pieces” Chapter 17 - Arpaci-Dusseau
  • Memory Alignment: “C Interfaces and Implementations” Chapter 5 - David Hanson

Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Strong C programming, understanding of pointers

Real world outcome:

// Replace libc malloc with yours
$ LD_PRELOAD=./mymalloc.so ./some_program

// Or link directly
$ gcc -o test test.c mymalloc.c

$ ./test
Allocated 1024 bytes at 0x7f1234000010
Allocated 2048 bytes at 0x7f1234000420
Freed 0x7f1234000010
Allocated 512 bytes at 0x7f1234000010 (reused!)
Coalesced free blocks: 1536 bytes available

Heap statistics:
  Total heap size: 65536 bytes
  Allocated: 2560 bytes
  Free: 62976 bytes (in 3 blocks)
  Fragmentation: 12%

Implementation Hints: Basic block structure:

struct block_header {
    size_t size;        // Size of this block (including header)
    int is_free;        // 1 if free, 0 if allocated
    struct block_header *next;  // Next block in free list
};

Allocation strategies:

  • First fit: Find first block that’s big enough (fast, more fragmentation)
  • Best fit: Find smallest block that fits (slower, less fragmentation)
  • Worst fit: Use largest block (sometimes good for preventing tiny fragments)

Coalescing: When freeing, check if adjacent blocks are also free. If so, merge them.

Learning milestones:

  1. Basic alloc/free works → You understand heap structure
  2. Coalescing prevents fragmentation → You understand memory management complexity
  3. Your allocator passes real-world tests → You understand production requirements

Project 4: Virtual Memory Simulator

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust, Python (for simulation)
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: Virtual Memory / Paging
  • Software or Tool: VM Simulator
  • Main Book: “Operating Systems: Three Easy Pieces” by Arpaci-Dusseau

What you’ll build: A simulator that implements virtual memory with page tables, address translation, page faults, and page replacement algorithms (FIFO, LRU, Clock). Visualize what happens when programs access memory.

Why it teaches OS fundamentals: Virtual memory is arguably the most important OS abstraction. It gives each process the illusion of its own private address space, enables memory protection, allows more memory than physically exists (via swapping), and enables memory-mapped files. Understanding page tables and TLBs is essential.

Core challenges you’ll face:

  • Address translation (virtual → physical) → maps to page tables
  • Multi-level page tables → maps to memory efficiency
  • Page fault handling → maps to demand paging
  • Page replacement algorithms → maps to when physical memory is full
  • TLB simulation → maps to caching translations

Key Concepts:

  • Paging: “Operating Systems: Three Easy Pieces” Chapters 18-22 - Arpaci-Dusseau
  • Page Tables: “Computer Systems: A Programmer’s Perspective” Chapter 9 - Bryant & O’Hallaron
  • Page Replacement: “Operating Systems: Three Easy Pieces” Chapter 22 - Arpaci-Dusseau
  • TLB: “Computer Architecture” Chapter 5 - Hennessy & Patterson

Difficulty: Expert Time estimate: 2-3 weeks Prerequisites: Understanding of binary, addressing, basic OS concepts

Real world outcome:

$ ./vm_simulator workload.trace --pages=16 --frames=4 --algorithm=lru

Virtual Memory Simulator
========================
Virtual pages: 16 (64KB address space with 4KB pages)
Physical frames: 4 (16KB physical memory)
Algorithm: LRU (Least Recently Used)

Processing memory trace...

Access: 0x1234 → Page 1
  [Page fault! Loading page 1 into frame 0]
  Page table: [1→0, -, -, -, ...]
  Physical frames: [P1, -, -, -]

Access: 0x5678 → Page 5
  [Page fault! Loading page 5 into frame 1]
  Page table: [1→0, -, -, -, -, 5→1, ...]
  Physical frames: [P1, P5, -, -]

Access: 0x1240 → Page 1
  [TLB hit! Translating to frame 0]
  Physical address: 0x0240

...

Final Statistics:
  Total accesses:     10,000
  Page faults:        847 (8.47%)
  TLB hits:           8,234 (82.34%)

Comparison with other algorithms:
  FIFO:  1,024 faults (10.24%)
  LRU:   847 faults (8.47%)
  Clock: 891 faults (8.91%)
  OPT:   723 faults (7.23%) [theoretical minimum]

Implementation Hints: Page table entry structure:

struct pte {
    unsigned int present : 1;   // Is page in physical memory?
    unsigned int frame : 20;    // Physical frame number
    unsigned int dirty : 1;     // Has page been written?
    unsigned int accessed : 1;  // For LRU approximation
};

Address translation:

uint32_t translate(uint32_t virtual_addr) {
    uint32_t page_num = virtual_addr >> 12;      // Upper bits
    uint32_t offset = virtual_addr & 0xFFF;      // Lower 12 bits

    if (!page_table[page_num].present) {
        handle_page_fault(page_num);
    }

    uint32_t frame = page_table[page_num].frame;
    return (frame << 12) | offset;
}

For LRU: maintain access timestamps or use a list where accessed pages move to front.

Learning milestones:

  1. Address translation works → You understand virtual → physical mapping
  2. Page faults handled correctly → You understand demand paging
  3. LRU beats FIFO → You understand replacement algorithm trade-offs

Project 5: Process Scheduler Simulator

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 3: Advanced (The Engineer)
  • Knowledge Area: Process Management / Scheduling
  • Software or Tool: Scheduler Simulator
  • Main Book: “Operating Systems: Three Easy Pieces” by Arpaci-Dusseau

What you’ll build: A scheduler simulator that implements multiple scheduling algorithms (FIFO, SJF, Round Robin, MLFQ, CFS) and compares their performance on different workloads. Visualize process execution timelines.

Why it teaches OS fundamentals: The scheduler is the heart of multiprogramming—it decides which process runs when. Different algorithms optimize for different goals (throughput, latency, fairness). Understanding scheduling helps you understand why your program sometimes runs slowly even on a fast CPU.

Core challenges you’ll face:

  • Process state machine → maps to ready, running, blocked states
  • Context switch simulation → maps to overhead of switching
  • Priority implementation → maps to fairness vs efficiency
  • Multi-level queues → maps to different process types
  • Metrics calculation → maps to turnaround time, response time, fairness

Key Concepts:

  • Scheduling Algorithms: “Operating Systems: Three Easy Pieces” Chapters 7-9 - Arpaci-Dusseau
  • Process States: “Operating Systems: Three Easy Pieces” Chapter 4 - Arpaci-Dusseau
  • Multi-Level Feedback Queue: “Operating Systems: Three Easy Pieces” Chapter 8 - Arpaci-Dusseau
  • Completely Fair Scheduler: “Linux Kernel Development” Chapter 4 - Robert Love

Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Understanding of processes, basic data structures

Real world outcome:

$ ./scheduler sim workload.txt --algorithm=mlfq

Process Scheduler Simulator
===========================
Workload: 5 processes
  P1: CPU burst 100ms
  P2: CPU burst 20ms, I/O 50ms, CPU burst 20ms
  P3: CPU burst 200ms
  P4: CPU burst 10ms
  P5: CPU burst 50ms

Algorithm: Multi-Level Feedback Queue (3 levels, quantum 10/20/40ms)

Timeline:
0ms    [P1 starts on Q0]
10ms   [P1 demoted to Q1] [P2 starts on Q0]
20ms   [P2 blocks on I/O]
20ms   [P3 starts on Q0]
30ms   [P3 demoted to Q1] [P4 starts on Q0]
40ms   [P4 completes!] [P5 starts on Q0]
...

Results:
          Turnaround  Response  Wait
  P1:     180ms       0ms       80ms
  P2:     110ms       10ms      40ms
  P3:     290ms       20ms      90ms
  P4:     40ms        30ms      30ms
  P5:     120ms       40ms      70ms

Average:  148ms       20ms      62ms

Comparison:
  Algorithm     Turnaround   Response   Fairness
  FIFO          220ms        72ms       Poor
  SJF           148ms        38ms       Poor (starvation)
  Round Robin   175ms        20ms       Good
  MLFQ          148ms        20ms       Good
  CFS           152ms        22ms       Excellent

Implementation Hints: Process Control Block (PCB):

struct process {
    int pid;
    enum { READY, RUNNING, BLOCKED, TERMINATED } state;
    int priority;
    int burst_remaining;
    int arrival_time;
    int start_time;  // For response time
    int completion_time;  // For turnaround time
};

MLFQ rules:

  1. New processes start at highest priority queue
  2. If process uses full quantum, demote to lower priority
  3. If process blocks before quantum, stay at same priority
  4. Periodic priority boost to prevent starvation

CFS (Linux’s scheduler) uses a red-black tree keyed by “virtual runtime”—processes that have run less get priority.

Learning milestones:

  1. FIFO and SJF work → You understand basic scheduling
  2. Round Robin achieves fairness → You understand time-slicing
  3. MLFQ adapts to workload → You understand why modern schedulers are complex

Project 6: Fork and Exec (Process Creation)

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust (with libc bindings)
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 2: Intermediate (The Developer)
  • Knowledge Area: Process Management / Unix API
  • Software or Tool: Process Explorer
  • Main Book: “The Linux Programming Interface” by Michael Kerrisk

What you’ll build: A tool that explores and demonstrates Unix process creation: forking, exec’ing, waiting, orphans, zombies, and the process tree. Understand the elegance of fork+exec separation.

Why it teaches OS fundamentals: Unix’s fork() + exec() model is fundamentally different from Windows’ CreateProcess(). Understanding this separation (fork = copy, exec = replace) reveals Unix’s philosophy: simple primitives that compose. This is why Unix shells are so powerful.

Core challenges you’ll face:

  • Understanding fork’s return value → maps to parent vs child distinction
  • Exec family (execve, execlp, etc.) → maps to program replacement
  • Wait and exit status → maps to parent-child synchronization
  • Orphan and zombie processes → maps to process lifecycle edge cases
  • Building a process tree visualizer → maps to process hierarchy

Key Concepts:

  • fork(): “The Linux Programming Interface” Chapter 24 - Michael Kerrisk
  • exec(): “The Linux Programming Interface” Chapter 27 - Michael Kerrisk
  • Process Relationships: “Advanced Programming in the UNIX Environment” Chapter 8 - Stevens & Rago
  • Unix vs Windows Process Creation: “Operating Systems: Three Easy Pieces” Chapter 5 - Arpaci-Dusseau

Difficulty: Intermediate Time estimate: 1 week Prerequisites: Basic C programming, familiarity with Unix

Real world outcome:

// fork_explorer output
$ ./fork_explorer

=== Demonstrating fork() ===
Parent PID: 1234
Calling fork()...

[Parent] fork() returned 1235 (child's PID)
[Child]  fork() returned 0 (I am the child)

=== Demonstrating fork + exec ===
Forking, then exec'ing /bin/ls...

[Parent] Waiting for child 1236...
[Child]  About to exec /bin/ls
file1.txt  file2.txt  fork_explorer
[Parent] Child 1236 exited with status 0

=== Process tree ===
$ ./fork_explorer tree

init(1)
├── systemd(456)
   ├── sshd(789)
      └── bash(1234)
          └── fork_explorer(1235)
   └── cron(790)
└── ...

=== Demonstrating zombies ===
Forking child that exits immediately...
Child 1237 has exited
[Before wait] ps shows: 1237 Z+ (zombie!)
[After wait] Zombie reaped

=== Why fork+exec is elegant ===
Want to redirect output? fork, then redirect, then exec.
Want to change directory? fork, then chdir, then exec.
The child can modify its own environment before exec!

Implementation Hints: The fork+exec pattern:

pid_t pid = fork();
if (pid == 0) {
    // Child: set up environment, then become another program
    close(1);  // Close stdout
    open("output.txt", O_WRONLY);  // Now fd 1 is the file!
    execl("/bin/ls", "ls", "-l", NULL);
    // exec doesn't return on success
    perror("exec failed");
    exit(1);
} else {
    // Parent: wait for child
    int status;
    waitpid(pid, &status, 0);
}

This is how shell redirection (ls > file) works! Redirect after fork, before exec.

Learning milestones:

  1. You understand fork’s magic → Parent and child continue from the same point
  2. You understand exec’s transformation → Process image completely replaced
  3. You see the elegance → Separation enables shell features like redirection

Project 7: Unix Shell from Scratch

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust, Go
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 2. The “Micro-SaaS / Pro Tool” (Solo-Preneur Potential)
  • Difficulty: Level 3: Advanced (The Engineer)
  • Knowledge Area: Process Management / Unix Philosophy
  • Software or Tool: Unix Shell
  • Main Book: “The Linux Programming Interface” by Michael Kerrisk

What you’ll build: A fully functional Unix shell with command execution, pipes, I/O redirection, background jobs, signal handling, and built-in commands. This is the ultimate Unix process management project.

Why it teaches OS fundamentals: The shell is where Unix philosophy comes alive. Every feature you implement teaches a fundamental concept: fork+exec for commands, pipes for IPC, redirection for file I/O, signals for job control. Building a shell teaches you how Unix really works.

Core challenges you’ll face:

  • Parsing command lines → maps to lexing and parsing
  • **Implementing pipes ( )** → maps to inter-process communication
  • I/O redirection (<, >, ») → maps to file descriptors
  • Background jobs (&) → maps to job control and signals
  • Signal handling (Ctrl+C, Ctrl+Z) → maps to asynchronous events

Key Concepts:

  • Pipes: “The Linux Programming Interface” Chapter 44 - Michael Kerrisk
  • I/O Redirection: “Advanced Programming in the UNIX Environment” Chapter 3 - Stevens & Rago
  • Signals: “The Linux Programming Interface” Chapters 20-22 - Michael Kerrisk
  • Job Control: “Advanced Programming in the UNIX Environment” Chapter 9 - Stevens & Rago

Difficulty: Advanced Time estimate: 3-4 weeks Prerequisites: Project 6, strong C skills

Real world outcome:

$ ./myshell
myshell> echo Hello, World!
Hello, World!

myshell> ls -la | grep .c | wc -l
5

myshell> cat input.txt > output.txt

myshell> sort < unsorted.txt | uniq > sorted.txt

myshell> sleep 100 &
[1] 1234

myshell> jobs
[1]  Running    sleep 100

myshell> fg 1
sleep 100
^C
[1]  Interrupted    sleep 100

myshell> cd /tmp
myshell> pwd
/tmp

myshell> export PATH=$PATH:/custom/bin

myshell> history
    1  echo Hello, World!
    2  ls -la | grep .c | wc -l
    ...

myshell> exit
Goodbye!

Implementation Hints: Pipeline implementation:

// For: cmd1 | cmd2
int pipefd[2];
pipe(pipefd);  // pipefd[0]=read, pipefd[1]=write

if (fork() == 0) {
    // cmd1: write to pipe
    close(pipefd[0]);
    dup2(pipefd[1], STDOUT_FILENO);
    close(pipefd[1]);
    exec(cmd1);
}

if (fork() == 0) {
    // cmd2: read from pipe
    close(pipefd[1]);
    dup2(pipefd[0], STDIN_FILENO);
    close(pipefd[0]);
    exec(cmd2);
}

// Parent closes both ends and waits
close(pipefd[0]);
close(pipefd[1]);
wait(NULL); wait(NULL);

For longer pipelines, chain multiple pipes. For job control, use setpgid() and tcsetpgrp().

Learning milestones:

  1. Basic commands execute → You understand fork+exec deeply
  2. Pipes work → You understand IPC
  3. Job control works → You understand signals and process groups
  4. You appreciate bash → You understand how complex a real shell is

Project 8: System Call Interface

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C + Assembly
  • Alternative Programming Languages: Rust (with inline asm)
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: Kernel Interface / System Programming
  • Software or Tool: Syscall Explorer
  • Main Book: “The Linux Programming Interface” by Michael Kerrisk

What you’ll build: A tool that makes system calls directly (bypassing libc), traces system calls made by programs, and implements a simple syscall wrapper library. Understand the user-kernel boundary.

Why it teaches OS fundamentals: System calls are the OS’s API—the only way user programs can request kernel services. Understanding how the CPU transitions from user mode to kernel mode, how arguments are passed, and how results are returned reveals the true interface between you and the OS.

Core challenges you’ll face:

  • Making raw syscalls without libc → maps to syscall ABI
  • Understanding the user/kernel boundary → maps to privilege levels
  • Tracing syscalls (like strace) → maps to ptrace and debugging
  • Implementing a syscall wrapper → maps to what libc actually does
  • Comparing Linux and Windows syscalls → maps to OS API design

Key Concepts:

  • System Calls: “The Linux Programming Interface” Chapter 3 - Michael Kerrisk
  • x86-64 Syscall Convention: “Low-Level Programming” Chapter 8 - Igor Zhirkov
  • ptrace for Tracing: “The Linux Programming Interface” Chapter 26 - Michael Kerrisk
  • User/Kernel Transition: “Linux Kernel Development” Chapter 5 - Robert Love

Difficulty: Expert Time estimate: 2 weeks Prerequisites: Assembly knowledge, understanding of kernel concepts

Real world outcome:

// syscall_explorer output

$ ./syscall_explorer raw

=== Making raw syscalls (no libc) ===

// write(1, "Hello\n", 6) using raw syscall
movq $1, %rax      // syscall number (write = 1)
movq $1, %rdi      // fd = 1 (stdout)
movq $msg, %rsi    // buffer
movq $6, %rdx      // count
syscall            // CPU transitions to kernel mode!

Result: Hello
Returned: 6 (bytes written)

$ ./syscall_explorer trace /bin/ls

=== Tracing syscalls of /bin/ls ===
execve("/bin/ls", ["ls"], [/* 50 vars */]) = 0
brk(NULL)                               = 0x5555557a0000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT
openat(AT_FDCWD, "/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF...", 832)              = 832
mmap(NULL, 1847776, PROT_READ)          = 0x7f1234000000
...
write(1, "file1.txt\nfile2.txt\n", 20)  = 20
close(1)                                = 0
exit_group(0)                           = ?

Total: 42 syscalls

=== Linux vs Windows ===
Operation          Linux Syscall    Windows API
---------          -------------    -----------
Write to file      write()          NtWriteFile()
Create process     fork()+exec()    NtCreateProcess()
Open file          openat()         NtCreateFile()
Memory allocation  mmap()           NtAllocateVirtualMemory()

Linux: ~450 syscalls, simple interface
Windows: ~400 NT syscalls, more complex, often wrapped by Win32 API

Implementation Hints: x86-64 Linux syscall convention:

  • rax: syscall number
  • rdi, rsi, rdx, r10, r8, r9: arguments 1-6
  • syscall instruction transitions to kernel
  • rax: return value

Raw syscall in inline assembly:

long my_write(int fd, const void *buf, size_t count) {
    long ret;
    asm volatile (
        "syscall"
        : "=a" (ret)
        : "a" (1), "D" (fd), "S" (buf), "d" (count)  // 1 = write
        : "rcx", "r11", "memory"
    );
    return ret;
}

For tracing, use ptrace(PTRACE_SYSCALL, ...) to stop at each syscall entry/exit.

Learning milestones:

  1. Raw syscall works → You understand the syscall ABI
  2. Tracer shows all syscalls → You understand what programs really do
  3. You see the OS’s true interface → You understand the kernel boundary

Project 9: File System from Scratch

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust, C++
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: File Systems / Storage
  • Software or Tool: Custom File System
  • Main Book: “Operating Systems: Three Easy Pieces” by Arpaci-Dusseau

What you’ll build: A simple file system (like ext2 or FAT) that runs in a disk image file. Implement superblock, inodes, data blocks, directories, and basic operations (create, read, write, delete, list). Then mount it with FUSE!

Why it teaches OS fundamentals: File systems are how the OS organizes persistent storage. Understanding inodes, block allocation, directories as special files, and journaling for crash recovery reveals how “files” become bytes on disk and back again.

Core challenges you’ll face:

  • Block layout design → maps to disk organization
  • Inode structure → maps to file metadata
  • Directory implementation → maps to name → inode mapping
  • Block allocation (bitmap or free list) → maps to space management
  • Path traversal → maps to hierarchical namespace

Key Concepts:

  • File System Implementation: “Operating Systems: Three Easy Pieces” Chapters 39-42 - Arpaci-Dusseau
  • Inodes and Directories: “Understanding the Linux Kernel” Chapter 12 - Bovet & Cesati
  • FUSE Interface: “The Linux Programming Interface” Chapter 14 - Michael Kerrisk
  • Crash Consistency: “Operating Systems: Three Easy Pieces” Chapter 42 - Arpaci-Dusseau

Difficulty: Expert Time estimate: 4-6 weeks Prerequisites: Strong C, understanding of file concepts

Real world outcome:

$ ./mkfs.myfs disk.img 10M

Creating MyFS filesystem on disk.img (10 MB)
Block size: 4096 bytes
Total blocks: 2560
Inode count: 256
Data blocks: 2048
Reserved: 512 (superblock, bitmaps, inode table)

Filesystem created!

$ ./fsck.myfs disk.img
MyFS filesystem check
Superblock: OK
Inode bitmap: OK
Block bitmap: OK
Root directory: OK
Filesystem clean.

$ ./myfs disk.img /mnt/myfs    # Mount with FUSE!

$ cd /mnt/myfs
$ echo "Hello, MyFS!" > hello.txt
$ cat hello.txt
Hello, MyFS!

$ mkdir subdir
$ ls -la
total 8
drwxr-xr-x  3 user user 4096 Jan 1 12:00 .
drwxr-xr-x  5 user user 4096 Jan 1 12:00 ..
-rw-r--r--  1 user user   14 Jan 1 12:00 hello.txt
drwxr-xr-x  2 user user 4096 Jan 1 12:00 subdir

$ # Raw disk dump
$ ./myfs-debug disk.img

Superblock:
  Magic: 0xMYFS
  Block size: 4096
  Inode count: 256
  Free inodes: 253
  Free blocks: 2044

Inode 2 (root directory):
  Type: directory
  Size: 4096
  Blocks: [100]
  Permissions: 0755

Block 100 (root directory data):
  Entry 0: "."      → inode 2
  Entry 1: ".."     → inode 2
  Entry 2: "hello.txt" → inode 3
  Entry 3: "subdir" → inode 4

Implementation Hints: Disk layout:

Block 0: Superblock (magic, sizes, free counts)
Block 1: Inode bitmap (1 bit per inode)
Block 2: Block bitmap (1 bit per data block)
Block 3-N: Inode table (inodes, ~128 bytes each)
Block N+: Data blocks

Inode structure:

struct inode {
    uint16_t mode;        // Permissions + type
    uint16_t uid, gid;    // Owner
    uint32_t size;        // File size
    uint32_t atime, mtime, ctime;
    uint32_t blocks[12];  // Direct block pointers
    uint32_t indirect;    // Indirect block pointer
};

Directories are just files containing (name, inode) pairs.

Learning milestones:

  1. Create and read files → You understand blocks and inodes
  2. Directories work → You understand the namespace abstraction
  3. FUSE mount works → You’ve built a real, usable file system!

Project 10: Everything Is a File (Device Files)

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 3: Advanced (The Engineer)
  • Knowledge Area: Device Drivers / Unix Philosophy
  • Software or Tool: Virtual Devices
  • Main Book: “Linux Device Drivers” by Corbet, Rubini & Kroah-Hartman

What you’ll build: A set of virtual device drivers using FUSE or kernel modules that implement the Unix “everything is a file” philosophy: a random number device, a counter device, a virtual sensor, and more.

Why it teaches OS fundamentals: The “everything is a file” abstraction is Unix’s killer feature. By implementing your own character devices, you’ll understand why this abstraction is so powerful: any program that can read/write files can interact with any device, no special APIs needed.

Core challenges you’ll face:

  • Character vs block devices → maps to device types
  • Implementing read/write handlers → maps to device operations
  • Device state management → maps to driver complexity
  • Creating device nodes → maps to /dev filesystem
  • Understanding major/minor numbers → maps to device identification

Key Concepts:

  • Device Drivers: “Linux Device Drivers” Chapters 1-3 - Corbet et al.
  • Character Devices: “Linux Device Drivers” Chapter 3 - Corbet et al.
  • /dev and udev: “The Linux Programming Interface” Chapter 14 - Michael Kerrisk
  • FUSE for Userspace Devices: FUSE documentation

Difficulty: Advanced Time estimate: 2 weeks Prerequisites: C programming, basic kernel concepts

Real world outcome:

$ # Your custom devices in /dev!

$ cat /dev/myrandom  # Your random number generator
8b2e7f4a1c3d9e6b

$ echo 100 > /dev/mycounter  # Set counter
$ cat /dev/mycounter
100
$ cat /dev/mycounter
101
$ cat /dev/mycounter
102

$ cat /dev/mytemp    # Virtual temperature sensor
23.5

$ echo "Hello" > /dev/myupper
$ cat /dev/myupper
HELLO

$ # Composability in action!
$ cat /dev/myrandom | xxd | head -5
00000000: 3862 3265 3766 3461 3163 3364 3965 3662  8b2e7f4a1c3d9e6b

$ # Works with ANY program that reads files!
$ python3 -c "print(open('/dev/mycounter').read())"
105

Implementation Hints: Using FUSE (simpler, userspace):

static int my_read(const char *path, char *buf, size_t size,
                   off_t offset, struct fuse_file_info *fi) {
    if (strcmp(path, "/random") == 0) {
        // Generate random hex string
        int len = snprintf(buf, size, "%08x\n", rand());
        return len;
    }
    if (strcmp(path, "/counter") == 0) {
        static int counter = 0;
        int len = snprintf(buf, size, "%d\n", counter++);
        return len;
    }
    return -ENOENT;
}

static struct fuse_operations my_ops = {
    .read = my_read,
    .write = my_write,
    .getattr = my_getattr,
};

For a kernel module (harder, more powerful):

static ssize_t dev_read(struct file *f, char __user *buf,
                        size_t len, loff_t *off) {
    // Copy data to userspace
    copy_to_user(buf, data, len);
    return len;
}

static struct file_operations fops = {
    .read = dev_read,
    .write = dev_write,
};

Learning milestones:

  1. Devices appear in /dev → You understand device files
  2. Read/write work → You understand the file abstraction for devices
  3. Standard tools work with your devices → You understand composability

Project 11: Inter-Process Communication

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust, Go
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool” (Solo-Preneur Potential)
  • Difficulty: Level 3: Advanced (The Engineer)
  • Knowledge Area: IPC / Concurrency
  • Software or Tool: IPC Explorer
  • Main Book: “The Linux Programming Interface” by Michael Kerrisk

What you’ll build: A comprehensive IPC explorer that demonstrates all Unix IPC mechanisms: pipes, named pipes (FIFOs), message queues, shared memory, semaphores, and Unix domain sockets. Compare their performance and use cases.

Why it teaches OS fundamentals: Processes are isolated by design (that’s protection!), but they often need to communicate. Each IPC mechanism has different trade-offs: pipes are simple, shared memory is fast, sockets are flexible. Understanding IPC is essential for building multi-process systems.

Core challenges you’ll face:

  • Pipes and FIFOs → maps to byte streams between processes
  • Shared memory → maps to zero-copy communication
  • Semaphores → maps to synchronization
  • Message queues → maps to typed message passing
  • Unix domain sockets → maps to socket API for local IPC

Key Concepts:

  • Pipes and FIFOs: “The Linux Programming Interface” Chapters 44-45 - Michael Kerrisk
  • Shared Memory: “The Linux Programming Interface” Chapters 48-50 - Michael Kerrisk
  • Semaphores: “The Linux Programming Interface” Chapters 47, 53 - Michael Kerrisk
  • Unix Domain Sockets: “The Linux Programming Interface” Chapter 57 - Michael Kerrisk

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Project 7, understanding of process concepts

Real world outcome:

$ ./ipc_explorer

=== Inter-Process Communication Comparison ===

Testing with 1 million 64-byte messages:

1. Pipe (unidirectional)
   Throughput: 2.3 GB/s
   Latency:    1.2 µs
   Use case:   Simple parent-child communication

2. Named Pipe (FIFO)
   Throughput: 2.1 GB/s
   Latency:    1.5 µs
   Use case:   Unrelated processes, persistent

3. Message Queue (POSIX)
   Throughput: 0.8 GB/s
   Latency:    3.2 µs
   Use case:   Priority messages, typed data

4. Shared Memory + Semaphore
   Throughput: 12.4 GB/s (!)
   Latency:    0.3 µs
   Use case:   High-performance, complex synchronization

5. Unix Domain Socket
   Throughput: 1.9 GB/s
   Latency:    1.8 µs
   Use case:   Bidirectional, socket API, file descriptor passing

=== Demo: Producer-Consumer with each mechanism ===

$ ./ipc_demo pipe
[Producer] Sending: "Hello from producer"
[Consumer] Received: "Hello from producer"

$ ./ipc_demo shm
[Producer] Wrote to shared memory at 0x7f1234000000
[Consumer] Read from shared memory: "Hello from producer"

$ ./ipc_demo socket
[Server] Listening on /tmp/ipc.sock
[Client] Connected
[Server] Received: "Hello from client"
[Client] Received response: "Hello from server"

Implementation Hints: Shared memory with semaphore:

// Create shared memory
int fd = shm_open("/myshm", O_CREAT | O_RDWR, 0666);
ftruncate(fd, SIZE);
void *ptr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

// Create semaphore for synchronization
sem_t *sem = sem_open("/mysem", O_CREAT, 0666, 0);

// Producer writes, then signals
memcpy(ptr, data, len);
sem_post(sem);

// Consumer waits, then reads
sem_wait(sem);
memcpy(local, ptr, len);

Unix domain sockets work like TCP but on the same machine—much faster and can pass file descriptors between processes!

Learning milestones:

  1. Pipes work for basic IPC → You understand byte stream communication
  2. Shared memory is much faster → You understand zero-copy trade-offs
  3. You can choose the right IPC → You understand when to use each mechanism

Project 12: Unix Philosophy Tools

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust, Go
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool” (Solo-Preneur Potential)
  • Difficulty: Level 2: Intermediate (The Developer)
  • Knowledge Area: Unix Philosophy / Text Processing
  • Software or Tool: Core Utilities
  • Main Book: “The Linux Command Line” by William Shotts

What you’ll build: Implement your own versions of classic Unix tools: cat, head, tail, wc, grep, sort, uniq, cut, tr. Then use them together in pipelines to see Unix philosophy in action.

Why it teaches OS fundamentals: These tools embody Unix philosophy: each does one thing well, reads from stdin, writes to stdout, and can be composed. Building them teaches you about file I/O, text processing, and why this simple pattern is so powerful.

Core challenges you’ll face:

  • Standard I/O handling → maps to stdin/stdout/stderr
  • Command-line argument parsing → maps to program interface
  • Efficient text processing → maps to buffered I/O
  • Filter pattern implementation → maps to Unix composability
  • Making them work in pipelines → maps to process cooperation

Key Concepts:

  • Standard I/O: “The Linux Programming Interface” Chapter 4 - Michael Kerrisk
  • Unix Philosophy: “The Art of Unix Programming” Chapter 1 - Eric S. Raymond
  • Text Processing: “The Linux Command Line” Chapters 6, 19 - William Shotts
  • Filter Programs: “Advanced Programming in the UNIX Environment” Chapter 15 - Stevens & Rago

Difficulty: Intermediate Time estimate: 2-3 weeks Prerequisites: C programming, understanding of I/O

Real world outcome:

$ # Your implementations work just like the real ones!

$ ./mycat file1.txt file2.txt
[Contents of file1.txt]
[Contents of file2.txt]

$ ./myhead -n 5 bigfile.txt
[First 5 lines]

$ ./mywc -l *.c
  125 main.c
   87 utils.c
  212 total

$ ./mygrep "error" log.txt
Line 45: error: file not found
Line 123: error: permission denied

$ # The magic: composability!
$ ./mycat access.log | ./mygrep "404" | ./mycut -d' ' -f7 | ./mysort | ./myuniq -c | ./mysort -rn | ./myhead -5

  1523 /missing-page.html
   892 /old-api/users
   445 /favicon.ico
   234 /wp-admin.php
   122 /api/v1/deprecated

$ # That's 6 of YOUR programs working together!
$ # Each did one thing. The shell connected them.
$ # This is Unix philosophy in action.

Implementation Hints: Filter program template:

int main(int argc, char *argv[]) {
    FILE *input = stdin;

    // If filename given, use that
    if (argc > 1) {
        input = fopen(argv[1], "r");
        if (!input) { perror(argv[1]); return 1; }
    }

    char line[4096];
    while (fgets(line, sizeof(line), input)) {
        // Process line
        // Write to stdout
        fputs(processed, stdout);
    }

    if (input != stdin) fclose(input);
    return 0;
}

This pattern works for cat, grep, sort, uniq, tr, etc. The key insight: stdin/stdout are the universal interface.

Learning milestones:

  1. Individual tools work → You understand file I/O
  2. They compose in pipelines → You understand Unix philosophy
  3. You see the elegance → Small tools + shell = infinite power

Project 13: Containers from Scratch

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Go, Rust
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 4. The “Open Core” Infrastructure (Enterprise Scale)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: Namespaces / Isolation
  • Software or Tool: Container Runtime
  • Main Book: “Container Security” by Liz Rice

What you’ll build: A minimal container runtime that uses Linux namespaces and cgroups to isolate processes—essentially, a mini-Docker. Understand how containers are NOT virtual machines, but isolated Linux processes.

Why it teaches OS fundamentals: Containers are the most important infrastructure innovation of the last decade, and they’re pure Unix/Linux magic. They use kernel features (namespaces for isolation, cgroups for resource limits) to create the illusion of separate systems. Understanding containers means understanding modern OS isolation.

Core challenges you’ll face:

  • PID namespace → maps to isolated process tree
  • Mount namespace → maps to isolated filesystem
  • Network namespace → maps to isolated network stack
  • User namespace → maps to uid/gid mapping
  • cgroups → maps to resource limits (CPU, memory)

Key Concepts:

  • Namespaces: “Container Security” Chapter 4 - Liz Rice
  • cgroups: “Container Security” Chapter 5 - Liz Rice
  • chroot and pivot_root: “The Linux Programming Interface” Chapter 18 - Michael Kerrisk
  • Linux Capabilities: “Container Security” Chapter 7 - Liz Rice

Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Deep Linux knowledge, C programming

Real world outcome:

$ sudo ./mycontainer run alpine /bin/sh

=== Creating container ===
[+] Creating namespaces: pid, mnt, uts, net, user
[+] Setting up cgroups: 512MB memory limit, 50% CPU
[+] Setting hostname to "container"
[+] Mounting container filesystem (alpine rootfs)
[+] Pivoting root
[+] Dropping capabilities
[+] Executing /bin/sh

/ # hostname
container

/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh   ← We are PID 1!
    2 root      0:00 ps aux

/ # cat /etc/os-release
NAME="Alpine Linux"    ← Different from host!

/ # ip addr
1: lo: <LOOPBACK> ...
   inet 127.0.0.1/8    ← Isolated network

/ # # Try to escape... nope!
/ # ls /host-root
ls: /host-root: No such file or directory

$ # From the host, it's just a process:
$ ps aux | grep mycontainer
root  12345  0.0  0.1  ... ./mycontainer run alpine /bin/sh

Implementation Hints: Container creation with namespaces:

int child_func(void *arg) {
    // We're in the new namespaces now!

    // Set hostname
    sethostname("container", 9);

    // Mount new root
    mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL);
    mount(rootfs, rootfs, "bind", MS_BIND | MS_REC, NULL);

    // Pivot root
    mkdir(put_old, 0700);
    pivot_root(rootfs, put_old);

    // Unmount old root
    umount2(put_old, MNT_DETACH);

    // Execute command
    execv(cmd, args);
}

int main() {
    int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUTS |
                CLONE_NEWNET | CLONE_NEWUSER | SIGCHLD;

    char *stack = malloc(STACK_SIZE) + STACK_SIZE;
    pid_t pid = clone(child_func, stack, flags, arg);

    // Set up cgroups for resource limits
    // ...

    waitpid(pid, NULL, 0);
}

cgroups: write to /sys/fs/cgroup/*/container-name/ to limit resources.

Learning milestones:

  1. Process isolation works → You understand namespaces
  2. Resource limits work → You understand cgroups
  3. You understand Docker → Containers are just fancy processes!

Project 14: xv6 Operating System Study

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: N/A (xv6 is specifically C)
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: Complete OS / MIT Teaching OS
  • Software or Tool: xv6 OS
  • Main Book: “xv6: a simple, Unix-like teaching operating system” (MIT)

What you’ll build: Study and modify xv6, MIT’s teaching operating system. Add a new system call, implement a new scheduling algorithm, add a new shell feature. Understand a complete, working Unix-like OS.

Why it teaches OS fundamentals: xv6 is a complete, readable, runnable Unix-like OS in about 10,000 lines of C. It has everything: boot, processes, memory, file system, pipes, shell. Unlike reading Linux source, xv6 is designed to be understood. This is the “see it all working together” project.

Core challenges you’ll face:

  • Understanding the boot process → maps to how OS starts
  • Adding a system call → maps to kernel interface
  • Modifying the scheduler → maps to CPU allocation
  • Understanding the file system → maps to persistent storage
  • Debugging kernel code → maps to systems debugging

Key Concepts:

  • Complete OS Structure: “xv6 Book” (MIT) - All chapters
  • Unix Implementation: “The Design of the Unix Operating System” - Maurice Bach
  • Kernel Debugging: “Operating Systems: Three Easy Pieces” Appendix - Arpaci-Dusseau
  • OS Architecture: “Operating Systems: Three Easy Pieces” - Arpaci-Dusseau

Difficulty: Expert Time estimate: 4-8 weeks Prerequisites: Most previous projects, strong C skills

Real world outcome:

$ cd xv6-riscv
$ make qemu

xv6 kernel is booting
hart 1 starting
hart 2 starting
init: starting sh

$ # You're inside xv6!
$ ls
.              1 1 1024
..             1 1 1024
README         2 2 2286
cat            2 3 23896
echo           2 4 22720
...

$ # Your modifications:

$ # 1. New system call: getcount (process syscall count)
$ getcount
Process 3: 47 system calls

$ # 2. New scheduler: lottery scheduling
$ ps
PID  TICKETS  STATE    NAME
1    10       running  init
2    20       ready    sh
3    5        sleeping cat

$ # 3. New shell feature: command history
$ history
1: ls
2: cat README
3: getcount

$ # Trace a system call through the whole stack:
# User calls write()
# → trap into kernel (ecall instruction)
# → syscall dispatcher looks up sys_write
# → sys_write validates and calls filewrite
# → filewrite calls the inode layer
# → inode layer calls the block layer
# → block layer calls the disk driver
# → bytes reach the disk!

Implementation Hints: Adding a system call:

  1. Add syscall number to kernel/syscall.h
  2. Add handler to kernel/syscall.c
  3. Implement the handler in a kernel file
  4. Add user-space wrapper in user/user.h and user/usys.pl

File system structure:

Block 0:    Boot sector
Block 1:    Superblock
Block 2-31: Inode blocks
Block 32+:  Bitmap + data blocks

Key files to study:

  • kernel/main.c: Boot sequence
  • kernel/trap.c: Interrupt/syscall handling
  • kernel/proc.c: Process management
  • kernel/vm.c: Virtual memory
  • kernel/fs.c: File system
  • kernel/bio.c: Block I/O

Learning milestones:

  1. You understand the boot process → Follow main.c through startup
  2. You can add a system call → You understand the user/kernel boundary
  3. You understand the file system → You can trace read() from user to disk
  4. You can modify the scheduler → You understand process management

Project 15: Kernel Module Development

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust (for Linux kernel)
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 3. The “Service & Support” Model (B2B Utility)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: Kernel Development / Device Drivers
  • Software or Tool: Linux Kernel Module
  • Main Book: “Linux Device Drivers” by Corbet, Rubini & Kroah-Hartman

What you’ll build: A Linux kernel module that adds functionality to the running kernel: a character device, a proc file, a system call, or a network filter. Experience actual kernel programming.

Why it teaches OS fundamentals: Kernel modules let you extend Linux without recompiling. You’ll experience kernel programming: no libc, no user-space safety, one bug = kernel panic. This is OS development in the real world.

Core challenges you’ll face:

  • Kernel build system → maps to kernel development workflow
  • Kernel memory allocation → maps to kmalloc, no malloc
  • Kernel synchronization → maps to spinlocks, mutexes in kernel
  • Proc filesystem interface → maps to /proc as kernel-user bridge
  • Surviving kernel crashes → maps to debugging without printf

Key Concepts:

  • Module Basics: “Linux Device Drivers” Chapter 2 - Corbet et al.
  • Character Devices: “Linux Device Drivers” Chapter 3 - Corbet et al.
  • Proc Filesystem: “Linux Device Drivers” Chapter 4 - Corbet et al.
  • Kernel Debugging: “Linux Kernel Development” Chapter 18 - Robert Love

Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Strong C, understanding of kernel concepts

Real world outcome:

$ # Build and load your module
$ make
$ sudo insmod mymodule.ko

$ dmesg | tail -3
[12345.678] mymodule: Hello from the kernel!
[12345.679] mymodule: Created /dev/mydev and /proc/myinfo

$ # Your character device
$ echo "test" > /dev/mydev
$ cat /dev/mydev
Received: test (5 bytes)

$ # Your proc file
$ cat /proc/myinfo
MyModule Statistics
-------------------
Opens: 3
Reads: 15
Writes: 2
Bytes processed: 1234

$ # Your module is part of the kernel!
$ lsmod | grep mymodule
mymodule    16384  0

$ # Clean up
$ sudo rmmod mymodule
$ dmesg | tail -1
[12400.123] mymodule: Goodbye from the kernel!

Implementation Hints: Basic kernel module:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

static int __init mymodule_init(void) {
    printk(KERN_INFO "mymodule: Hello!\n");
    // Create devices, register handlers, etc.
    return 0;
}

static void __exit mymodule_exit(void) {
    printk(KERN_INFO "mymodule: Goodbye!\n");
    // Clean up
}

module_init(mymodule_init);
module_exit(mymodule_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("You");
MODULE_DESCRIPTION("My first kernel module");

Key differences from user space:

  • No libc: use kernel functions (kmalloc, not malloc)
  • No floating point!
  • Sleep carefully: msleep, not sleep
  • Concurrency is harder: use spinlocks, RCU, etc.

Learning milestones:

  1. Module loads and unloads → You understand the kernel module system
  2. Device or proc file works → You’ve written kernel code that users can interact with
  3. You survive kernel crashes gracefully → You’re a kernel developer!

Project 16: Network Stack Exploration

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C
  • Alternative Programming Languages: Rust, Go
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 4: Expert (The Systems Architect)
  • Knowledge Area: Networking / Protocol Implementation
  • Software or Tool: Network Stack
  • Main Book: “TCP/IP Illustrated, Volume 1” by W. Richard Stevens

What you’ll build: Implement a minimal TCP/IP stack: raw socket handling, IP packet parsing, ARP, ICMP (ping), and basic TCP (connection establishment). Understand how network “just works.”

Why it teaches OS fundamentals: Networking is a core OS responsibility. Understanding how packets flow from application to wire and back—through sockets, protocol layers, and device drivers—reveals one of the OS’s most complex subsystems. The network stack is also where “everything is a file” meets real-world complexity.

Core challenges you’ll face:

  • Raw socket programming → maps to bypassing the kernel stack
  • Ethernet frame handling → maps to layer 2
  • IP packet parsing/creation → maps to layer 3
  • ICMP implementation → maps to network diagnostics
  • TCP state machine → maps to reliable transport

Key Concepts:

  • TCP/IP Layers: “TCP/IP Illustrated, Volume 1” Chapter 1 - Stevens
  • IP Protocol: “TCP/IP Illustrated, Volume 1” Chapters 5-8 - Stevens
  • TCP Protocol: “TCP/IP Illustrated, Volume 1” Chapters 17-24 - Stevens
  • Raw Sockets: “The Linux Programming Interface” Chapter 58 - Michael Kerrisk

Difficulty: Expert Time estimate: 4-6 weeks Prerequisites: Strong C, understanding of networking concepts

Real world outcome:

$ # Your minimal TCP/IP stack!

$ sudo ./mynet interface tap0

=== MyNet TCP/IP Stack ===
Listening on tap0 (10.0.0.2/24)

$ # From another terminal:
$ ping 10.0.0.2

[MyNet] Received ARP request: Who has 10.0.0.2?
[MyNet] Sending ARP reply: 10.0.0.2 is at aa:bb:cc:dd:ee:ff
[MyNet] Received ICMP Echo Request from 10.0.0.1
[MyNet] Sending ICMP Echo Reply to 10.0.0.1
[MyNet] Received ICMP Echo Request from 10.0.0.1
[MyNet] Sending ICMP Echo Reply to 10.0.0.1
^C

$ # TCP connection!
$ nc 10.0.0.2 8080 <<< "Hello"

[MyNet] Received TCP SYN from 10.0.0.1:45678 → :8080
[MyNet] Sending TCP SYN+ACK
[MyNet] Received TCP ACK - Connection established!
[MyNet] TCP state: ESTABLISHED
[MyNet] Received TCP data: "Hello\n" (6 bytes)
[MyNet] Sending TCP ACK
[MyNet] Received TCP FIN
[MyNet] Sending TCP FIN+ACK
[MyNet] TCP state: CLOSED

$ # See the packet structure:
$ ./mynet dump

IP Header:
  Version: 4
  IHL: 5 (20 bytes)
  Total Length: 60
  TTL: 64
  Protocol: 6 (TCP)
  Source: 10.0.0.1
  Destination: 10.0.0.2

TCP Header:
  Source Port: 45678
  Dest Port: 8080
  Sequence: 0x12345678
  Flags: SYN
  Window: 65535

Implementation Hints: Packet structures:

struct eth_header {
    uint8_t dest[6];
    uint8_t src[6];
    uint16_t type;  // 0x0800 = IP, 0x0806 = ARP
} __attribute__((packed));

struct ip_header {
    uint8_t version_ihl;
    uint8_t tos;
    uint16_t total_length;
    uint16_t id;
    uint16_t flags_fragment;
    uint8_t ttl;
    uint8_t protocol;  // 1 = ICMP, 6 = TCP, 17 = UDP
    uint16_t checksum;
    uint32_t src_ip;
    uint32_t dest_ip;
} __attribute__((packed));

Use TAP devices or raw sockets to send/receive Ethernet frames. Implement checksum calculation for IP and TCP headers.

Learning milestones:

  1. ARP works → You understand MAC address resolution
  2. ICMP ping works → You understand IP and network diagnostics
  3. TCP handshake works → You understand reliable transport

Capstone Project: Write Your Own Operating System

  • File: OPERATING_SYSTEMS_FROM_FIRST_PRINCIPLES.md
  • Main Programming Language: C + Assembly
  • Alternative Programming Languages: Rust + Assembly, C++ + Assembly
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold” (Educational/Personal Brand)
  • Difficulty: Level 5: Master (The First-Principles Wizard)
  • Knowledge Area: Complete Operating System
  • Software or Tool: Custom OS
  • Main Book: “Operating Systems: Three Easy Pieces” by Arpaci-Dusseau

What you’ll build: A complete (minimal) operating system from scratch: bootloader, kernel, memory management, process management, file system, and shell. This is the ultimate test of OS understanding.

Why it teaches everything: This capstone integrates every concept: boot (Project 1-2), memory (Projects 3-4), processes (Projects 5-6), files (Project 9), IPC (Project 11), and the Unix philosophy (Projects 7, 10, 12). Building a complete OS means you truly understand how computers work.

Core challenges you’ll face:

  • Bootstrapping → maps to getting from power-on to kernel
  • Memory management → maps to heap, virtual memory, protection
  • Process management → maps to creation, scheduling, termination
  • File system → maps to persistent storage organization
  • User/kernel separation → maps to protection and system calls
  • Shell → maps to user interface

Key Concepts:

  • Everything from previous projects!
  • Integration: “Operating Systems: Three Easy Pieces” - Full book
  • OS Design Decisions: “The Design of the Unix Operating System” - Maurice Bach
  • Implementation Details: “Operating Systems Design and Implementation” - Tanenbaum

Difficulty: Master Time estimate: 3-6 months Prerequisites: All previous projects

Real world outcome:

$ qemu-system-x86_64 -drive format=raw,file=myos.img

=== MyOS v1.0 ===
Bootloader: Loaded kernel at 0x100000
Kernel: Initializing GDT, IDT...
Kernel: Initializing memory manager (128 MB detected)
Kernel: Initializing process scheduler
Kernel: Mounting root filesystem
Kernel: Starting init process (PID 1)
Init: Starting shell on /dev/tty0

myos$ uname -a
MyOS 1.0 x86_64 MyKernel

myos$ ls /
bin/
dev/
etc/
home/
proc/

myos$ cat /proc/meminfo
Total:     128 MB
Used:      12 MB
Free:      116 MB
Buffers:   4 MB

myos$ ps
PID   STATE     MEM     CMD
1     running   256K    /bin/init
2     running   512K    /bin/sh

myos$ echo "Hello from MyOS!" > /home/hello.txt
myos$ cat /home/hello.txt
Hello from MyOS!

myos$ # Pipes work!
myos$ ls /bin | wc -l
12

myos$ # Background processes work!
myos$ sleep 10 &
[1] 3
myos$ ps
PID   STATE     MEM     CMD
1     running   256K    /bin/init
2     running   512K    /bin/sh
3     sleeping  128K    /bin/sleep

myos$ # You built this. All of it.

Implementation Hints: Start with the OSDev wiki tutorials, but implement everything yourself:

  1. Boot: Real mode → protected mode → long mode (64-bit)
  2. Memory: Physical page allocator → virtual memory (paging) → heap allocator
  3. Interrupts: Set up IDT, handle timer, keyboard
  4. Processes: Task structure, context switch in assembly, simple scheduler
  5. System calls: Software interrupt, dispatch table
  6. File system: Start with ramfs, then persistent fs on disk
  7. Shell: Parse commands, fork+exec, pipes

Milestones:

  • Week 1-2: Boot to protected mode, print to screen
  • Week 3-4: Interrupts, keyboard input
  • Week 5-6: Memory management
  • Week 7-10: Processes and scheduling
  • Week 11-14: File system
  • Week 15+: Shell, utilities, polish

Learning milestones:

  1. Kernel boots and prints → You understand the boot process
  2. Processes run and switch → You understand CPU virtualization
  3. Memory is protected → You understand isolation
  4. Files persist → You understand storage abstraction
  5. Shell runs commands → You’ve built a complete OS!

Project Comparison Table

Project Difficulty Time Unix Depth Fun Factor Practical Value
1. Bare Metal Hello Expert Weekend ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
2. Bootloader Expert 1-2 weeks ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
3. Memory Allocator Advanced 2 weeks ⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐
4. Virtual Memory Sim Expert 2-3 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
5. Scheduler Simulator Advanced 2 weeks ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
6. Fork and Exec Intermediate 1 week ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
7. Unix Shell Advanced 3-4 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
8. System Call Interface Expert 2 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
9. File System Expert 4-6 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
10. Device Files Advanced 2 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
11. IPC Mechanisms Advanced 2-3 weeks ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐
12. Unix Philosophy Tools Intermediate 2-3 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
13. Containers Expert 3-4 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
14. xv6 Study Expert 4-8 weeks ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
15. Kernel Module Expert 3-4 weeks ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
16. Network Stack Expert 4-6 weeks ⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐
Capstone: Your OS Master 3-6 months ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

Phase 1: Hardware-Software Boundary (4-6 weeks)

Goal: Understand what happens before the OS even loads

  1. Bare Metal Hello World - See the raw hardware
  2. Bootloader - Load your own kernel

Phase 2: Memory (4-6 weeks)

Goal: Understand how memory becomes usable

  1. Memory Allocator - Build malloc/free
  2. Virtual Memory Simulator - Understand paging

Phase 3: Processes (6-8 weeks)

Goal: Understand how programs run concurrently

  1. Scheduler Simulator - Understand scheduling algorithms
  2. Fork and Exec - The Unix process model
  3. Unix Shell - Put it all together

Phase 4: The Unix Philosophy (4-6 weeks)

Goal: Understand why Unix works the way it does

  1. Unix Philosophy Tools - Build cat, grep, sort
  2. IPC Mechanisms - How processes communicate
  3. Everything Is a File - Device files

Phase 5: Deep System (6-8 weeks)

Goal: Understand kernel internals

  1. System Call Interface - The kernel boundary
  2. File System - Persistent storage

Phase 6: Modern Unix (4-6 weeks)

Goal: Understand modern Linux

  1. Containers - Namespaces and cgroups
  2. Kernel Modules - Extend the kernel

Phase 7: Complete Understanding (8-12 weeks)

Goal: See it all work together

  1. xv6 Study - Understand a complete OS
  2. Network Stack - Understand networking

Phase 8: Mastery (3-6 months)

Goal: Prove you understand it all

  1. Capstone: Your Own OS - Build it from scratch

Start Here Recommendation

Given your goal to understand OS fundamentals and the Unix philosophy:

Start with Project 6: Fork and Exec

Why?

  • It’s accessible—you can start immediately with just C knowledge
  • It reveals the heart of Unix’s elegance
  • You’ll understand why shells work the way they do
  • It builds directly into Project 7 (Shell)

Then do Project 12: Unix Philosophy Tools

Why?

  • Building cat, grep, sort shows why “one thing well” works
  • Composing them in pipelines is an “aha!” moment
  • You’ll see why text streams are powerful

After these two, you’ll have deep intuition for the Unix philosophy. Then tackle the lower-level projects (1-5) to understand what’s under the hood.


Why Unix Won: The Definitive Explanation

After completing these projects, you’ll understand this deeply:

1. SIMPLICITY COMPOUNDS
   - Simple tools compose into complex systems
   - Complex tools don't compose
   - 50 years later, `cat | grep | sort` still works

2. TEXT IS UNIVERSAL
   - Programs don't need to understand each other
   - Just read lines, process, write lines
   - JSON, YAML, config files—all text

3. FILES ARE EVERYTHING
   - One interface for devices, pipes, sockets, processes
   - Programs written for files work with everything
   - No special APIs for special cases

4. FORK+EXEC ENABLES SHELLS
   - Redirect after fork, before exec
   - No need for shell to understand programs
   - Any new program automatically works

5. WORSE IS BETTER
   - Simple, incomplete solution that ships
   - Beats perfect solution that doesn't
   - Unix shipped. Others didn't.

The internet, cloud, containers, DevOps—all built on Unix
Because when you bet on simplicity, you bet on the future.

Summary

# Project Name Main Language
1 Bare Metal “Hello World” (No OS) Assembly (x86 NASM)
2 Bootloader that Loads a Kernel Assembly + C
3 Memory Allocator (malloc from Scratch) C
4 Virtual Memory Simulator C
5 Process Scheduler Simulator C
6 Fork and Exec (Process Creation) C
7 Unix Shell from Scratch C
8 System Call Interface C + Assembly
9 File System from Scratch C
10 Everything Is a File (Device Files) C
11 Inter-Process Communication C
12 Unix Philosophy Tools C
13 Containers from Scratch C
14 xv6 Operating System Study C
15 Kernel Module Development C
16 Network Stack Exploration C
Capstone Write Your Own Operating System C + Assembly

“An operating system is the layer that says ‘you shall not’ to programs that want to do dangerous things, and ‘yes, here you go’ to programs that ask nicely through system calls. Understanding this boundary—between user and kernel, between process and process, between program and hardware—is understanding the computer itself.”