Systems · Memory · C / C++

From Written Code
to Execution on the Machine

The complete road — how a line of code becomes something the CPU runs, and how memory (pages, frames, the page table) makes it all fit. Now with diagrams.

Built from Anup's own questions on paging — bundled, illustrated, and completed end-to-end.
On Disk Page — a chunk of your program Frame — a slot in physical RAM
1
The starting line

You write the source code

It begins as plain text in a .cpp file on your disk. The CPU cannot run text — it has no idea what int main() means yet.

// main.cpp  — just text on disk
int main() {
    int x = 42;
    return 0;
}

Nothing in RAM. Nothing running. Raw material.

2
Turning text into a machine

Compile & link → an executable

The compiler turns each source file into machine-code object files. The linker stitches them together (plus libraries) into one executable binary.

main.cpp ──▶ compiler ──▶ main.o ┐
util.cpp ──▶ compiler ──▶ util.o ┤──▶ linker ──▶ ./program  (binary on disk)
                          libc, libstdc++ ┘

Still a file on disk — but now it's CPU-runnable instructions. (The compiler/linker part of your Cherno track is the on-ramp to this road.)

3
Press run

The OS creates a process + virtual space

The OS creates a process and hands it a private virtual address space — a made-up range of addresses only this program can see.

You asked: “Is virtual address space a non-living, imaginary thing? Does it hold RAM?”

Yes — it's imaginary, and no — it does not hold RAM. It stores nothing itself. It's just a set of address numbers (labels) the program is allowed to use — like house numbers on a street with no houses built yet. The real bytes live in RAM frames; the page table translates a virtual number into the real RAM spot.

Virtual space = labels · RAM = the actual storage
VIRTUAL addresses (just numbers) PHYSICAL RAM (real bytes) 0x0000 (empty label) 0x1000 (empty label) 0x2000 (empty label) Frame 7 ← real bytes Frame 2 ← real bytes Frame 9 ← real bytes The dashed labels hold nothing. Follow the arrow (page table) to reach the real storage.
4
The shape of your program

The memory layout (virtual)

Inside that virtual space, your program is arranged into the regions you know. This tidy picture is the virtual layout — how it looks to the program:

high address  ┌──────────────┐
              │    Stack     │  ↓ grows down  — locals, function calls
              │      ⋮       │
              │     Heap     │  ↑ grows up    — new / malloc
              │  BSS / Data  │                — globals & statics
low  address  │  Text (code) │                — your instructions
              └──────────────┘

Every region is made of pages. Remember: this whole neat layout is virtual — the real bytes are scattered across RAM.

5
The core idea

Why divide the program into pages?

You asked: “Why does the process need to be divided into pages at first?”

So it doesn't need one big contiguous block of RAM. Chop the program into equal-size pages, and each page goes into any free slot, anywhere.

Parking lot: without paging your program is one long bus needing many spots in a row. Paging cuts it into equal cars; each parks in any free spot. Suddenly it fits.

Bonuses unlocked: virtual memory (run programs bigger than RAM), isolation & protection, and sharing (one library copy used by many programs).

★
The problem paging kills

External fragmentation

You asked: “What is external fragmentation — and why ‘external’, not ‘internal’? Is it a method like paging?”

It's wasted RAM you get without paging. Programs load and quit, leaving scattered holes. Total free can be big, yet no single hole is large enough for a contiguous request.

Why the names: where the waste sits
EXTERNAL — waste BETWEEN blocks P1 gap P2 gap P3 gap Free space exists, but it's scattered in the gaps → a big contiguous request can't fit. Waste is outside the blocks. INTERNAL — waste INSIDE the last block one frame (4 KB) used (3 KB) wasted Waste is inside an allocated frame — the leftover of the last page.
Paging's fix: drop the “contiguous” rule. Cut the request into pages; each page fills a scattered hole. External fragmentation gone. ✅

Is external fragmentation a method like paging? No — it's a problem, not a technique. You never choose “external fragmentation vs paging.” The old contiguous-allocation method suffers from it; paging is the method that cures it.

Methods you choose · vs · Problems that happen
METHODS (you choose) PROBLEMS (side effects) Contiguous allocation Paging Segmentation External fragmentation Internal fragmentation suffers avoids ✗ has a little Paging is the cure; external fragmentation is the disease. Not two options you pick between.
6
Two words, two roles

Pages vs Frames — one ball, one box

You asked: “Is a page in a frame like many small balls in one box, or one big ball in one box?”

One big ball that fills the box exactly. Page size = frame size, so one page completely fills one frame — never many pages in a frame, never one page across two frames. Strictly one-to-one.

One page fills one frame — exactly
Frame (4 KB slot) ONE page fills it exactly ✓ correct Frame ✗ not many small pages
The rule: a page's bytes are stored inside a frame, and one frame holds exactly one page. So a code page, a stack page, and each heap page all sit in different frames.

Can 2 KB of stack + 2 KB of heap share one page in one frame?

You asked: exactly this. Answer: normally no.

Even though 2 + 2 = 4 KB (one page's worth), stack and heap are not next to each other. Stack sits at the top of the address space, heap near the bottom, with a huge gap between. A page only ever holds bytes from one continuous stretch of virtual addresses — and stack & heap aren't continuous. So each gets its own page → its own frame. Minimum: 2 pages, 2 frames.

Different regions can't share a page
virtual layout (far apart) Stack (high addr) …big gap… Heap (low addr) its own frame (e.g. F12) its own frame (e.g. F3) Two things from the SAME region, sitting side by side, CAN share a page. Different regions cannot.
7
The glue

The page table — the connector

You asked: “If the pages are scattered, how are they connected?”

By the page table — a per-process list mapping program's page → real frame. Pages are connected through this table, not through location (see the big-picture diagram at the top).

Hotel front desk: you ask for “page 3,” don't know the room. The desk (page table) checks the list: “page 3 → room 47.” The connection is the list, not the layout.
8
How a lookup happens

Address translation: page number + offset

When your program touches an address, the CPU's MMU splits it in two:

   Virtual address  =  [ page number | offset ]
                            │             │
                       which page    how far into it

Step 1: page number → page table → frame number.  Step 2: frame number + offset → real physical address.

Example — 4 KB pages, program reads virtual address 12,290:
  12,290 ÷ 4096  =  page 3, remainder 2   (offset)
  page table:  page 3  →  frame 47
  physical address  =  start of frame 47  +  2   ✅
The magic: program walks pages in order (contiguous, virtual); hardware redirects each to its real frame. Non-contiguous in RAM, contiguous to the program.

Speed note (TLB): the CPU caches recent page→frame translations in the TLB, so most accesses skip the table lookup entirely.

9
Loading — lazily

Demand paging (frames on first touch)

The OS loads pages on demand — only when used. Touch a page that isn't in RAM → page fault → OS grabs a free frame, loads the bytes from disk, updates the table, resumes you — invisibly.

For your heap: when you new/malloc 30 KB, physical frames are often handed out only as you write to each page. No RAM burned on untouched memory.
10
When RAM fills up

Swapping & virtual memory

RAM full and a new page needed? The OS evicts a cold page to disk (swap), freeing its frame. If that page is needed again later, a page fault brings it back.

RAM full ──▶ evict cold page ──▶ disk (swap) ──▶ frame free ──▶ load needed page
This is “virtual memory”: why a program can use more memory than you physically have. Too much swapping = “thrashing” (disk is far slower).
11
You're not alone in RAM

What else lives in frames

You asked: “What else can sit in other frames, beyond my program's layout?”

Your pages are one tenant. Frames hold everything the machine is doing — see the mix on the right side of the big-picture diagram: other processes' pages, the OS kernel, the page tables themselves, shared libraries (one libc for many programs), the page cache (files cached from disk), I/O / DMA buffers, and free frames waiting to be handed out.

Apartment building: RAM is the building; your program is one tenant. The page table keeps every tenant's things private and findable.
12
The finish line

The CPU executes

The CPU repeats fetch → decode → execute: ask for the next instruction by virtual address → MMU (via TLB / page table) translates to a frame → bytes fetched from RAM → instruction runs. Every access goes through the same translation.

Full circle: the neat layout is virtual (pages, ordered); the real bytes live in frames (physical, scattered); the page table connects the two — a clean private machine on top of a shared messy RAM.

🧮 Worked example — your 30 KB heap

With 10 KB pages (easy math):

In pages (virtual)

Heap page 0  (0–10 KB)
Heap page 1  (10–20 KB)
Heap page 2  (20–30 KB)

In frames (physical RAM)

Frame 3  ← Heap page 0
Frame 41 ← Heap page 1
Frame 9  ← Heap page 2

30 KB heap → 3 pages → each backed by a frame, scattered, connected by the page table. Program still sees one clean 30 KB heap. (Real pages are usually 4 KB, so 30 KB ≈ 8 pages/frames — same idea. With demand paging, a frame is handed out only on first write to that page.)

📎 One-line cheat sheet

Virtual space
Imaginary range of address numbers; stores nothing. It does NOT hold RAM — it points to it.
Page
An equal-size chunk of that virtual space (content).
Frame
An equal-size slot in physical RAM; holds exactly one page (container).
Page = frame size
One page fills one frame exactly — one-to-one, one big ball per box.
Page table
Per-process map: virtual page → physical frame. The connector.
MMU
Hardware that translates virtual → physical on every access.
Virtual address
= page number (which page) + offset (how far into it).
TLB
Tiny cache of recent translations so most lookups skip the table.
External frag.
A PROBLEM (not a method): wasted gaps between blocks → paging cures it.
Internal frag.
Wasted space inside the last page of a region → paging causes a little.
Demand paging
Get a frame only on first use, via a page fault.
Swapping
Push cold pages to disk when RAM is full = virtual memory.

🎯 The whole road in one breath

You write code → compiler + linker make an executable on disk → the OS starts a process with a private, imaginary virtual layout → it's cut into pages so it needn't be contiguous (killing external fragmentation) → each page is stored in one frame in RAM → the page table connects page → frame → the MMU translates every address (page number + offset) so scattered RAM looks contiguous → pages load on demand and overflow to disk when RAM is full → the CPU fetches from frames and executes.