Segmentation carves a program up the way a programmer thinks about it: separate, variable-sized segments for code, data, the heap, and the stack — not uniform blocks. Each segment is recorded in a segment table by its base (where it starts in physical memory) and its limit (how long it is).
Paging vs segmentation
Paging splits memory into fixed-size pieces the *hardware* cares about. Segmentation splits it into variable-size pieces the *program* cares about. That's why a segment can carry meaningful per-segment protection — mark the code segment read-only, the stack read/write.
A logical address names a segment and an offset
Every address is a pair: which segment, and an offset into it. To translate, the hardware looks up that segment's row, checks the offset against the limit, and — only if it's in bounds — adds the base. An offset that meets or exceeds the limit is out of bounds and traps as a segmentation fault.
// Each row is [base, limit]
const segTable = [
[1400, 1000], // segment 0
[6300, 400], // segment 1
[4300, 400], // segment 2
];
function translate(seg, offset) {
const [base, limit] = segTable[seg];
if (offset < 0 || offset >= limit) throw new Error("segmentation fault");
return base + offset;
}
Worked example
1Address (segment 2, offset 53): segment 2 has base 4300, limit 400.
3Address (segment 2, offset 450): is 450 < 400? No.
4The bounds check fails, so the access is rejected as a segmentation fault — the program tried to read past the end of its segment.
Segmentation re-introduces external fragmentation
Because segments vary in size, freeing them leaves scattered gaps of free memory that are too small to reuse — external fragmentation, the very thing fixed-size paging avoids. Real systems often page *within* segments to get both worlds.
OperationTimeSpace
Address translation · one base/limit entry per segment + a bounds checkO(1)O(segments)
Check yourself
Segment 2 has base 4300 and limit 400. What happens for logical address (segment 2, offset 450)?