Thus far, we have assumed that an address space is unrealistically small and fits into the physical
memory. In fact, we have been assuming that every address space of ervery running process fits
into memory. We will now relax these big assumptions, and assume that we wish to support many
concurrently-running large address space. To do so, we require an additional level in the memory
hierarchy. Thus far, we have assumed that all page tables in physical memory. However, to support
large address space, the OS will need a place to stash away portions of address spaces that currently
are not in great demand. In general, the characteristics of such a location are that it should have more
capacity than memory; as a result, it is generally slower (if it were faster, we would just use it as
memory, no?). In modern systems, this role is usually served by a hard disk dirve. Thus in our memory
hierarchy, big and slow hard drive sit at the bottom, with memory just above. And thus, we arrive
at the crux of the problem: How to go beyond physical memory? How can the OS make use of a larger,
slower device to transparently provide the illusion of a large virtual address space ? One question you
might have: why do we want to support a single large address space for a process ? Once again, the
answer is covenience and ease of use. With a large address space, you do not have to worry about
if there is room enough in memory for your program's data structure; rather, you just write the program
naturally, allocating memory as needed. It is a powerful illusion that the OS provides; and makes your
life vastly simpler. You are welcome! A contrast is found in older systems that used memory overlays,
which require programmers to manually move pieces of code or data in and out of memory as they
were needed. Try imagining what this would like: before calling a function or accessing some data,
you need to first arrange for the code or data to be in memory. Yuck!
Beyond just a single process, the addition of swap space allows the OS to support the illusion of a large
virtual memeory for multiple concurrently-running processes. The invention of multiprogramming (running
multiple programs at once; to better utilize the machines) almost demanded the ability to swap out some
pages, as early machines clearly could not hold all the pages needed by all processes at once. Thus, the
combination of multiprogramming and ease-of-use leads us to want to support using more memory than
is physically available. It is something that all modern VM systems; it is now something we will learn more
about.
Swap Space: The first thing we will need to do is to reserve some space on the disk for moving pages back
and forth. In operating systems, we generally refer to such space as swap space, because we swap pages
out of memory to it and swap pages into memory into it. Thus, we will simply assume that the OS can read
from and write to the swap space, in page-sized units. To do so, the OS will need to remember the disk address
of a given page. The size of the swap space is important, as ultimately if determines the maximum numner
of memory pages that can be in use by a system at a given time. We should note that swap space is not the
only on disk location for swapping traffic. For example, assume you are running a program binary (e.g. ls or
your own main program). The code pages from this binary are initially found on disk, and when the program
runs, they are loaded into memory (either all at once when the program starts execution, or as in modern
systems, one page at a time when needed). However, if the system needs to make room in physical memory
for other needs, it can safely re-use the mmeory space for these code pages, knowing that it can later swap
them in again from the on disk binary in the file system.
Present Bit: Now that we have some space on the disk, we need to add some machinery higher up in the system
in order to support swapping pages to and from the disk. Let us assume, for simplicity, that we have a system
with a hardware-managed TLB. Recall first that what happens on a memory reference. The running process
generates virtual memeory reference (for instruction fetches, or data accesses) and, in this case, the hardware
translates them into physical addresses before fetching the desired data from memory. Remember that the
hardware first extracts the VPN from the virtual address, checks the TLB for a match (a TLB hit), and if a hit,
produces the resulting physical address and fetches it from memory. This is hopefully the common case, as it
is fast (requiring no additional memory access). If the VPN is not found in the TLB (a TLB miss), the hardware
locates the page table in memory (using the page table base register) and looks up the page table entry (PTE)
for this page using the VPN as an index. If the page is valid and present in physical memory, the hardware
extracts the PFN from the PTE, installs it in the TLB, and retries the instruction, this time generating a TLB hit
; so far, so good. If we wish to allow pages to be swapped to disk, however, we must add even more machinery.
Specifically, when the hardware looks in the PTE, it may find that the page is not present in physica memory.
The way the hardware (or the OS, in the software-managed TLB approach) determines this is through a new
piece of information in each page table entry, known as the present bit. If the present bit is set to one, it means
the page is present in physical memory and everything proceeds as above.; if it is set to zero, the page is not
in memory but rather on disk somewhere. The act of accessing a page that is not in physical memory is commonly
referred to a page fault. Upon a page fault, the OS is invoked to service the page fault. A popular piece of code,
known as a page fault handler, runs and must service the page fault, as we now describe.