handmade.network » Wiki » Virtual Memory

Overview

Virtual memory allows processors to translate all memory addresses before they are accessed. This means the address sent to the physical RAM will be changed before the write or fetch actually occurs.

This is primarily useful because it allows the operating system to keep processes in separate virtual address spaces. Each process will have its own virtual address space - a set of linear to physical address mappings. The operating system will only include mappings to regions of memory that the process allocated.

Furthermore, the operating system can be alerted when a process tries to access memory it shouldn't. For example, an exception may be raised if a process attempts to read to a linear address that doesn't have an associated physical address. The operating system can then decide if it wants to terminate the process, or create the mapping.

Methods

Segmentation

In segmentation the virtual address space is split into segments. Each memory access will require a segment register to be specified. Each segment will have an associated base address and size. As long as the address is lower than the size, the processor will add the base address to it, and perform the memory access.

Because only privileged code can modify the segment registers, a process is only able to use memory within the segment registers it is given.

Paging

This is the method used in most modern processors.

The virtual address space is divided in pages. On most processors, a page must have one of a few available sizes, and must be aligned at a boundary the same as its size. On x86_64 processors, the smallest page size is 4KiB, and this is what most operating systems use by default.

Each page is assigned a physical address, or is marked as not-present. This means the operating system can control, at a given granularity, the mapping of any linear address in the virtual address space. Each page can also be given security attributes such as readable, writable, executable or a combination thereof.

When a process attempts to access a page that isn't present, it will cause a page fault exception. If the process has allocated memory within the region the address is in, the operating system has an opportunity to map physical memory to the address. This may involve loading data from secondary storage, such as in the case of a memory-mapped file.

TLB

It is difficult for a processor to work out which physical address corresponds to a linear address when using paging. For this reason, most modern processors store a cached copy of some of the mappings in a translation lookaside buffer. Instead of traversing tables containing information about the mappings, the processor can just use an entry in the TLB.

When the operating system changes the physical address to a page is mapped or ones of its attributes, it must invalidate the page. This informs the processor that if it has cached information about the page in the TLB it will be incorrect. On computers with multiple processors (or cores) the operating system will have to ensure that all processors are informed of the change through the use of inter-processor interrupts.

Each time the virtual address space is changed, such as when the running process changes, all the entries in the TLB must be removed. Because of the cost of this, some new processors allow entries in the TLB to be tagged with a PCID, so that entries can remain in it over several context switches.

Bugs

Paging allows for both kernel and process-specific pages to be mapped into a single virtual address space, as it provides security attributes to each page, meaning a given page can only be accessed from certain code. However, due to bugs in some processors' pipelines, it is possible to read from any page without the correct permissions: read more. This has caused some kernels to completely separate kernel and user address spaces, at the cost of additional address space switches.