Introduction
Segmentation is a memory management scheme that supports the programmer's logical view of a program: rather than one flat, arbitrarily numbered stream of bytes, a program is seen as a collection of variable-length, meaningful segments -- for example, main program code, a library function, the stack, the heap, and a symbol table. Each segment has a name (in practice, a segment number) and a length that is determined by its actual content, unlike pages, which are all fixed and equal in size regardless of what they hold.
Cricket analogy: A team's tour dossier isn't one flat file but distinct meaningful sections -- the batting lineup, the bowling attack, the fielding plan, the injury list -- each sized to however much content it actually needs, unlike a fixed-size scorecard page that's the same length regardless of what's on it.
Explanation
A logical address under segmentation is a pair: (segment number s, offset d). The OS maintains a segment table per process, where each entry stores a base (the starting physical address of that segment) and a limit (the segment's length). To translate an address, the hardware indexes the segment table with s, checks that 0 <= d < limit (raising a protection fault if not), and computes physical_address = base + d. The key contrast with paging is size and meaning: pages are fixed-size, hardware-defined chunks with no relationship to program structure, while segments are variable-size, logically meaningful units chosen by the compiler or programmer -- this makes segmentation a natural fit for sharing (e.g., sharing a read-only code segment among processes) and for applying different protection bits (read/write/execute) per segment, since an entire segment such as code can be marked read-execute-only while the stack is read-write. Because segments vary in size, segmentation reintroduces external fragmentation, the very problem paging was designed to avoid; many real systems therefore combine the two as segmentation with paging, where each segment is itself paged internally.
Cricket analogy: A player's stats are looked up by (innings number, ball number): the scorer indexes the innings table, checks the ball number is within that innings's actual ball count (raising a foul if not), and computes the exact delivery -- unlike pages, an innings varies in length depending on how the match actually unfolds, making it natural to share a bowling-figures section across formats or restrict who can edit the declared-innings record, though variable innings lengths can create awkward gaps in the schedule, which is why some leagues combine fixed-over blocks within variable innings.
Example
#include <stdio.h>
typedef struct {
unsigned int base;
unsigned int limit;
} SegmentTableEntry;
int translate(int segment, unsigned int offset, SegmentTableEntry *seg_table, unsigned int *phys_out) {
SegmentTableEntry entry = seg_table[segment];
if (offset >= entry.limit) {
printf("Protection fault: offset %u exceeds limit %u for segment %d\n",
offset, entry.limit, segment);
return -1;
}
*phys_out = entry.base + offset;
return 0;
}
int main(void) {
/* Segment 0 = code, Segment 1 = stack, Segment 2 = heap */
SegmentTableEntry seg_table[3] = {
{4300, 1200}, /* code: base 4300, length 1200 bytes */
{6500, 400}, /* stack: base 6500, length 400 bytes */
{8000, 2500} /* heap: base 8000, length 2500 bytes */
};
unsigned int physical;
if (translate(1, 350, seg_table, &physical) == 0)
printf("Segment 1, offset 350 -> physical address %u\n", physical);
translate(1, 450, seg_table, &physical); /* exceeds stack limit of 400 */
return 0;
}Output
The first call translates (segment 1, offset 350). Since 350 < limit (400), it is valid; physical address = base(6500) + offset(350) = 6850. The second call, (segment 1, offset 450), fails because 450 >= 400, so the hardware raises a protection fault before any memory is touched -- this is exactly the kind of out-of-bounds access (e.g., a stack overflow) that segment limits are designed to catch, something a flat single-base/limit contiguous scheme could only do for the whole process, not per logical unit.
Cricket analogy: Checking the 350th ball bowled against an innings limit of 400 balls, it's valid, so the delivery is logged at the correct spot in the innings record; but checking the 450th ball against that same 400-ball limit fails immediately -- the scorer flags an error before recording anything, exactly the kind of over-the-limit mistake a single flat match tally could never catch per innings.
Key Takeaways
- A segmented logical address is (segment number, offset); a segment table stores base and limit per segment.
- Physical address = segment_table[s].base + d, only valid if 0 <= d < segment_table[s].limit.
- Segments are variable-size and logically meaningful (code, stack, heap); pages are fixed-size and have no relation to program structure.
- Segmentation naturally supports per-segment protection (e.g., read-execute code, read-write data) and sharing of common segments across processes.
- Because segments vary in size, segmentation can suffer external fragmentation; many systems use paged segmentation to combine paging's fragmentation-free allocation with segmentation's logical structure.
Practice what you learned
1. What two values does a segment table entry store for each segment?
2. In segmentation, a logical address is represented as which pair?
3. What is the fundamental difference between a page and a segment?
4. Using the segment table {code: base 4300 limit 1200, stack: base 6500 limit 400, heap: base 8000 limit 2500}, what happens when a process references (segment 1, offset 450)?
Was this page helpful?
You May Also Like
Paging
Splitting logical and physical memory into fixed-size pages and frames to eliminate external fragmentation.
Contiguous Memory Allocation
Allocating each process a single unbroken block of physical memory, and the fragmentation problems that follow.
Memory Management Basics
How an OS tracks, allocates, and protects the memory used by processes running on a system.