Base and bounds relocated the whole address space as one slab. Look again at what that slab mostly contains:
click a region marked β for details
The space between stack and heap is not being used β yet it occupies physical memory anyway when the whole space is relocated. One base/bounds pair is wasteful, and it canβt even run a program whose address space exceeds physical memory.
The Crux: How To Support A Large Address Space
How do we support a large address space with (potentially) a lot of free space between the stack and the heap? A 32-bit space is 4GB; a typical program touches megabytes β yet base/bounds demands the entire space be resident.16.1 Segmentation: Generalized Base/Bounds
The fix β dating to the very early 1960s β is segmentation segmentation Virtualizing memory with a base-and-bounds register pair per logical segment (code, heap, stack) instead of one pair for the whole address space, so each segment can be placed independently in physical memory and unused gaps cost nothing. defined in ch. 16 β open in glossary : instead of one base/bounds pair in the MMU mmu The memory management unit β the part of the CPU that translates addresses; grows from two registers to TLBs and page-table walkers. defined in ch. 15 β open in glossary , have one per logical segment. A segment segment A contiguous portion of the address space of a particular length β canonically code, heap, or stack β that the hardware relocates and bounds-checks as a unit. defined in ch. 16 β open in glossary is just a contiguous portion of the address space of a particular length; our canonical space has three β code, heap, and stack. Now each can be placed independently in physical memory:
click a region marked β for details
Only used memory gets physical space, so huge, mostly-empty ( sparse sparse address space An address space that is large but mostly unused (e.g. a huge free gap between heap and stack); segmentation accommodates it by allocating physical memory only for the used segments. defined in ch. 16 β open in glossary ) address spaces become affordable. The hardware is exactly what youβd guess: three base/bounds pairs (Figure 16.3 β the register table under the translator below), where each size register plays the old bounds role.
Translation, per segment. Virtual address 100 sits in the code segment: the hardware adds it to codeβs base β 100 + 32KB = 32868 β checks 100 < 2KB β, and issues the reference. But virtual address 4200 (in the heap) sets a trap: naΓ―vely adding 4200 to the heap base 34KB gives 39016 β the wrong address. We must first extract the offset into the heap: the heap begins at virtual 4KB, so 4200 β 4096 = 104, and 34KB + 104 = 34920. And a reference to 7KB or beyond β past the end of the heap? Out of bounds, trap, likely termination. Try all of them:
virtual address
100
00000001100100
MMU (segmentation)
1. top 2 bits = 00 β Code
2. offset = VA & 0xFFF = 100
3. bounds:Β 100 < 2048 β
4. PA = 32 KB (32768) + 100 = 32868
physical address
32868
| Segment | selector | Base | Size | Grows positive? |
|---|---|---|---|---|
| Code β | 00 | 32 KB (32768) | 2 KB (2048) | 1 |
| Heap | 01 | 34 KB (34816) | 3 KB (3072) | 1 |
| Stack | 11 | 28 KB (28672) | 2 KB (2048) | 0 (backwards) |
The tour: 100 β codeβs byte 100 β 32868. 4200 β heap byte 104 β 34920. 3000 β codeβs quadrant but past its 2KB size β fault (the hole between segments!). 7168 (7KB) β one byte past the heap β the bookβs famous violation. 9000 β selector 10, which no segment backs. 15360 (15KB) β the stack, which grows BACKWARDS β the negative-offset trick is the next pageβs business, but the answer (27KB) is already right.
Aside: The Segmentation Fault
The term segmentation fault segmentation fault The OS's response to an illegal memory access β "you did something wrong with memory, foolish programmer" (name explained with segmentation). defined in ch. 14 β open in glossary (or violation) arises from exactly this: a memory access on a segmented machine to an illegal address. Humorously, the term persists even on machines with no segmentation support at all. Or not so humorously, if you canβt figure out why your code keeps faulting.16.2 Which Segment Are We Referring To?
How does the hardware know an addressβs segment, and its offset within it? The common explicit approach (used by VAX/VMS): chop the address space by the top few bits of the virtual address. Three segments need two bits; our 14-bit address splits like this β hereβs 4200:
The 14-bit virtual address 4200: top two bits select the segment, bottom twelve are the offset
01 = heapOffset β 0000 0110 1000 = 0x068 = 104
Selector 00 β use codeβs base/bounds; 01 β heap; 11 β stack. The bottom 12 bits are the offset β no subtraction needed β and the offset eases the bounds check too: just compare it against the segmentβs size. If base and bounds were arrays indexed by segment, the hardware would do:
// get top 2 bits of 14-bit VA
Segment = (VirtualAddress & SEG_MASK) >> SEG_SHIFT
// now get offset
Offset = VirtualAddress & OFFSET_MASK
if (Offset >= Bounds[Segment])
RaiseException(PROTECTION_FAULT)
else
PhysAddr = Base[Segment] + Offset
Register = AccessMemory(PhysAddr)
For our example: SEG_MASK = 0x3000, SEG_SHIFT = 12,
OFFSET_MASK = 0xFFF. Watch the same arithmetic run in the translator
above β the binary readout under the virtual address is this exact
split.
Two costs come bundled with the convenience. With three segments and two bits, one selector pattern (10) backs no segment β a quarter of the virtual space, gone (some systems merged code into the heapβs segment and spent only one bit). And each segment gets a hard maximum size β here 4KB, a 16KB space chopped into four β so a heap or stack that wants to grow beyond it is out of luck.
There are other ways: in the implicit approach, the hardware infers the segment from how the address was formed β generated by the program counter? code segment. Based off the stack or base pointer? stack. Anything else? heap.
But wait β preset 15360 above already whispered it: the stack doesnβt even grow the same direction as everything else. That takes one more bit of hardware, next.
Check yourself
1.Why is one base-and-bounds pair for the whole address space wasteful β and what exactly does segmentation change?
2.The heap's base register holds 34KB. Translate virtual address 4200 (the heap begins at virtual 4KB).
3.In the explicit approach with a 14-bit virtual address and 2 selector bits, which costs come bundled with the convenience?
4.A process references virtual address 3000 β inside its 16KB address space, in the gap after the 2KB code segment and before the heap begins at 4KB. What happens?
5.So where does the dreaded term 'segmentation fault' actually come from?