How Did x86 Segmentation Differ from Flat Paging?

In earlier x86 protected mode architectures, memory segmentation and flat paging represented two fundamentally different ways of organizing, protecting, and translating memory addresses. Segmentation divided memory into variable-sized logical regions defined by descriptor tables, whereas flat paging neutralized segmentation by mapping a uniform linear address space onto fixed-size physical memory frames.

Memory Segmentation in Early Protected Mode

When Intel introduced protected mode with the 80286 and expanded it to 32 bits with the 80386, segmentation was the primary mechanism for memory isolation. Programs did not reference physical memory directly. Instead, they used a logical address composed of a 16-bit segment selector and an offset.

The CPU used the selector to look up a segment descriptor in either the Global Descriptor Table (GDT) or a Local Descriptor Table (LDT). Each descriptor specified a base address, a segment limit, and access rights such as privilege levels (rings 0 through 3) and read, write, or execute permissions. The hardware computed the linear address using:

\[\text{Linear Address} = \text{Segment Base} + \text{Offset}\]

If the offset exceeded the segment limit or violated the access rights, the processor raised a general protection fault. Because segments could be any size up to \(64\text{ KiB}\) on the 80286 or \(4\text{ GiB}\) on the 80386, segmentation matched logical program structures like code, data, and stack. However, variable-sized segments made physical memory allocation difficult and led to external fragmentation.

Flat Paging on the 80386 and Later

With the 80386, Intel added hardware paging alongside segmentation. Operating systems soon discovered that combining full segmentation with paging added unnecessary complexity, so they adopted a flat memory model. In this model, the operating system configured code and data segment descriptors with a base address of \(0\) and a limit of \(4\text{ GiB}\), effectively making the logical offset equal to the linear address.

With paging enabled via the CR0 control register, the CPU translated the 32-bit linear address into a physical address using fixed-size blocks called pages, typically \(4\text{ KiB}\) (\(4096\text{ bytes}\)) each. The 32-bit linear address was split into three parts:

  • A 10-bit Page Directory index
  • A 10-bit Page Table index
  • A 12-bit offset within the \(4\text{ KiB}\) page (\(2^{12} = 4096\))

Page table entries stored the physical frame address along with per-page protection bits, such as present, read/write, and user/supervisor flags.

Key Architectural Differences

The shift from pure segmentation to flat paging changed how operating systems managed memory in several critical ways:

  • Granularity: Segmentation used variable-sized regions tailored to program modules, while paging used uniform \(4\text{ KiB}\) blocks.
  • Fragmentation: Segmentation suffered from external fragmentation as variable-sized blocks were allocated and freed. Paging eliminated external fragmentation, leaving only minor internal fragmentation within partially used pages.
  • Virtual Memory and Swapping: Swapping an entire variable-sized segment to disk was slow and complex. Paging allowed operating systems to swap individual \(4\text{ KiB}\) pages on demand.
  • Portability: Because other processor architectures lacked x86-style segmentation, using a flat paged model allowed operating systems like Linux and Windows NT to share a consistent memory management design across different CPUs.