Memory Segmentation — Address Translation, Protection & Sharing
Why Segmentation Feels Natural
You never think of your program as one flat array of bytes. You think in units: "the code", "the stack", "this array", "the sqrt routine". Segmentation lets the hardware see memory the same way — as a collection of variable-length, individually-protected, independently-growable pieces, each addressed by a segment number and an offset inside it.
The User's View — Memory as Segments
The program sees an orderly list of segments; physical memory holds each one wherever a free hole was found — in a different order, with gaps between. The segment table is the map that ties the two views together, one row per segment.
The Logical Address — <s, d>
Unlike a flat linear address, a segmented address is a pair. The segment number s says which region; the offset d says how far into it. The hardware never lets d escape its segment — that single check is what makes protection so cheap.
The Segment Table & Its Registers
Each row holds a base, a limit and permission bits. STBR tells the MMU where this process's table lives (reloaded on every context switch); STLR holds how many segments exist, so a bogus segment number s ≥ STLR traps before any lookup.
Address Translation — The MMU at Work
Every memory reference costs two accesses: one to fetch the descriptor (base, limit) and one for the data itself. The hardware validates s < STLR and d < limit; pass both and it emits base + d, fail either and it raises a trap. A TLB caches recent descriptors so the extra read nearly vanishes.
Translating <2, 53> Step by Step
| Address | Check (d < limit) | Result |
|---|---|---|
| <2, 53> | 53 < 400 ✓ | 4300 + 53 = 4353 |
| <0, 1200> | 1200 < 1000 ✗ | TRAP — offset too big |
| <7, 0> | 7 ≥ STLR (5) | TRAP — no such segment |
Protection — Bits That Stop Attacks
Marking the data and stack segments non-executable (the NX bit, sold as DEP on Windows and W^X on BSD) means an attacker who smuggles code into a buffer can't run it — the CPU traps the moment execution jumps there. The same descriptor that translates the address also enforces the permission, so the check is free.
Sharing — One Library, Many Processes
Both processes keep a sqrt row in their own table, and both rows carry the same base (6300). There is exactly one physical copy of the library; 50 processes sharing a 400 KB libc cost 400 KB total, not 20 MB. Because the shared segment is read-only (R+X), no process can corrupt it for the others.
The Catch — External Fragmentation
Because each segment needs a single contiguous region, free space scattered into holes of 150, 120 and 180 can't hold a 400 KB segment even though 450 KB is free in total. Segmentation cures internal fragmentation (segments are exact-sized) but keeps external fragmentation — which is why real systems pair it with paging.
Six Translations, One Segment Table
Segment table — 0: base 219, limit 600 · 1: 2300 / 14 · 2: 90 / 100 · 3: 1327 / 580 · 4: 1952 / 96. STLR = 5.
| Logical <s, d> | Validation | Physical address |
|---|---|---|
| <0, 430> | 430 < 600 ✓ | 219 + 430 = 649 |
| <1, 10> | 10 < 14 ✓ | 2300 + 10 = 2310 |
| <2, 500> | 500 < 100 ✗ | TRAP — offset > limit |
| <3, 400> | 400 < 580 ✓ | 1327 + 400 = 1727 |
| <4, 112> | 112 < 96 ✗ | TRAP — offset > limit |
| <5, 20> | 5 ≥ STLR (5) | TRAP — no such segment |
Only 649, 2310 and 1727 are legal physical addresses. Two attempts overran their segment's limit, and one named a segment that doesn't exist — each caught by the same two guards, s < STLR and d < limit.
Boundary Cases That Trip Students
Segment table — 0: base 1200, limit 500 · 1: 2400 / 200 · 2: 0 / 800 · 3: 3200 / 400.
| Logical <s, d> | Validation | Physical address |
|---|---|---|
| <0, 222> | 222 < 500 ✓ | 1200 + 222 = 1422 |
| <1, 199> | 199 < 200 ✓ | 2400 + 199 = 2599 |
| <2, 800> | 800 < 800 ✗ | TRAP — limit is exclusive |
| <3, 50> | 50 < 400 ✓ | 3200 + 50 = 3250 |
A valid offset runs 0 to limit − 1. So <1, 199> is the very last legal byte of segment 1, while <2, 800> traps — the offset must be strictly less than the limit. Segment 2 starting at base 0 is legal too: a base of zero is just a segment placed at the bottom of RAM.
Segmentation vs Paging
| Aspect | Segmentation | Paging |
|---|---|---|
| Block size | Variable — one per segment | Fixed — one page (≈ 4 KB) |
| Programmer visibility | Yes — natural units | Invisible |
| Address form | <s, d> — two-part | <p, d> — transparent |
| External fragmentation | Yes | No |
| Internal fragmentation | None | Up to one page |
| Sharing granularity | Whole segments | Individual pages |
| Protection granularity | Per-segment | Per-page |
| Table size | Small (few rows) | Large (millions) |
Segmentation gives meaning and protection; paging gives fragmentation-free placement. Modern CPUs use segmentation with paging — a program is split into logical segments, and each segment is then paged into fixed frames. You keep the programmer's model and lose external fragmentation.
Segmentation in the Real World
Eight Rules for Segmentation
Memory That Matches Your Mind
From the two-part address and the segment table, through MMU translation, protection bits and library sharing, to external fragmentation and the segmentation-vs-paging trade-off — you can translate any <s, d> by hand and explain why the model still lives inside every modern CPU.
Segmentation solves meaning; paging solves placement. Next comes paging in depth — page tables, the TLB and multi-level tables — and then how segmentation and paging combine to build virtual memory.
🧩 End of tutorial · Press ← to review, or click Restart