Virtual memory is all about making use of address space.
The address space of a processor refers the range of
possible addresses that it can use when loading and storing to
memory. The address space is limited by the width of the
registers, since as we know to load an address we need to issue a
load
instruction with the address
to load from stored in a register. For example, registers that
are 32 bits wide can hold addresses in a register range from
0x00000000
to
0xFFFFFFF
.
2^32 is equal to 4GB, so a 32 bit
processor can load or store to up to 4GB of memory.
New processors are generally all 64-bit processors, which as the name suggests has registers 64 bits wide. As an exercise, you should work out the address space available to these processors (hint: it's big!).
64-bit computing does have some trade-offs against using smaller bit-width processors. Every program compiled in 64-bit mode requires 8-byte pointers, which can increase code and data size, and hence impact both instruction and data cache performance. However, 64-bit processers tend to have more registers, which means less need to save temporary variables to memory when the compiler is under register pressure.
While 64-bit processors have 64-bit wide registers,
systems generally do not implement all 64-bits for
addressing — it is not actually possible to do
load
or
store
to all 16 exabytes of
theoretical physical memory!
Thus most architectures define an unimplemented region of the address space which the processor will consider invalid for use. x86-64 and Itanium both define the most-significant valid bit of an address, which must then be sign-extended (see the section called “Sign-extension”) to create a valid address. The result of this is that the total address space is effectively divided into two parts, an upper and a lower portion, with the addresses in-between considered invalid. This is illustrated in Figure 6.1, “Illustration of canonical addresses”. Valid addresses are termed canonical addresses (invalid addresses being non-canonical).
The exact most-significant bit value for the processor can usually be found by querying the processor itself using its informational instructions. Although the exact value is implementation dependent, a typical value would be 48; providing 248 = 256 TiB of usable address-space.
Reducing the possible address-space like this means that significant savings can be made with all parts of the addressing logic in the processor and related components, as they know they will not need to deal with full 64-bit addresses. Since the implementation defines the upper-bits as being signed-extended, this prevents portable operating systems using these bits to store or flag additional information and ensuring compatibility if the implementation wishes to implement more address-space in the future.
As with most components of the operating system, virtual memory acts as an abstraction between the address space and the physical memory available in the system. This means that when a program uses an address that address does not refer to the bits in an actual physical location in memory.
So to this end, we say that all addresses a program uses are virtual. The operating system keeps track of virtual addresses and how they are allocated to physical addresses. When a program does a load or store from an address, the processor and operating system work together to convert this virtual address to the actual address in the system memory chips.