By using base and bounds register, the OS can easily relocate processes to different parts of physical memory. However, there might be a huge chunk of “free” space wasted between the stack and heap(the internal fragmentation)

Segmentation: Generalized Base/Bounds

To solve the described problem, the idea of segmentation was born: instead of having just one base and bounds pair in our MMU, why not have a base and bounds pair per logical segment of the address space?

Segment → a contiguous portion of the address space of a particular length. And in our canonical space, we have 3 segments: code, stack and heap.

When allocating the memory, we can just put those segments in use into the physical memory while keeping track of their bases and bounds

Which Segment Are We Referring To?

The Explicit Approach

For the hardware to know which segment a virtual address is at, the hardware simply takes the first $log_2n$ bits (n is the number of segments) to determine which segment register to use, and then takes the next rest as the offset into the segment.

The Implicit Approach

The hardware determines the segment by noticing how the address was formed. If it’s coming from a program counter(instruction fetch), it’s in the code segment. If it’s based off the stack or base pointer, it’s in the stack. All the other address must be in heap

In addition to storing base and size for each segment, we must also consider other useful information: does the segmentation grows positively or negatively? (stack vs heap) When sharing, what permission should we give out? So we can have one bit to record the growth direction and some other protection bits to protect each segment.

And when there’s more than 3 segments, a segment table comes in handy. it usually support the creation of a very large number of segments, and thus enable a system to use segments in more flexible ways than we have thus far discussed.

There’s also a problem called external fragmentation, where the actual physical memory quickly becomes full of little holes of free space when there’s too many segments. One solution to this problem would be to compact physical memory by rearranging the existing segments. For example, the OS could stop whichever processes are running, copy their data to one contiguous region of memory, change their segment register values to point to the new physical locations, and thus have a large free extent of memory with which to work.