Prepared by Andrew Yoon for a lecture given by Professor Paul Eggert on 11/20/13
Performance of VM not quite as good as RAM, and we must rely on locality of reference: if a program refers to location x, its next reference is probably x, x-1, or x+1.
Say we have 128 KiB of RAM and 16 MiB of VM.
Example of a program's memory references 16 MiB VM ----------------------------------------------- | | x | | | | | x | | | | | | | x | Time | | | x | | | x | | | V | | | x |
We can store just the used portions of VM in our 128 KiB of RAM.
A problem with this is thrashing, where the 'actual' picture looks like this:
16 MiB VM ----------------------------------------------- | | x | | | | | | x | | | x | | | x | Time | | | | x | | x | | x | V | | x | |
And we don't have enough room in our RAM to store all the references. In this case I/O operations dominate paging. This is too slow! To the user it may appear that the program doesn't work at all!
As we can see, VM doesn't provide too much of a performance boost.
So why use VM at all? In practice, it is used to solve the following problem.
Lots of unreliable processes with bad memory references. Examples:
a[i], i out of range
*p, p points to
NULL, is freed, or is uninitialized
p = (char *) i, i is a random int or pointer
char a[n], if n is very large we will run out of memory, or if n is negative we will corrupt the stack
gcc -fsanitize=address
(p >> 3) + shadow_mem_startand the shadow memory will tell us if it is valid.
Example RAM--------------------------------------------------------- | p4 | p1 |(unused)| p2 | p3 |(reserved for kernel)| ------------------------------------------------------------ L1 U1 L0 U0
gcc -fPIC
Example Segment Table | Loc | Size | 0 | | | | | | Segment # . . . 255 | | |(The location column refers to physical memory addresses.)
RAM--------------------------------------------------------- | p3,s3 | p3,s0 | p3,s1 | etc | p3,s2 |(reserved for kernel)| ------------------------------------------------------------
Example 1-level page table (per process) -----32 bits wide---- 0 | 20 bits | 12 bits | . . . 2^20 - 1 | | | 2^22 bytes/page table = 4 MiB/process
%cr3points to the page table. You cannot see your own page table, so it is secure.
We can have multiple level page tables, such as the doubly indirect block we used in Lab 3. For example, if we are using a 2-level page table, our virtual address could look like this:
| 10 bits | 10 bits | 12 bits | "hi" "med" "lo"
hi points to level 1 of the table, med points to level 2.
/* * pmap(va) * gives physical memory at virtual address va */ unsigned pmap(unsigned va) { unsigned hi = va >> 22; unsigned med = (va >> 12) & ((1 << 10) - 1); unsigned* l1pt = asm("%cr3" .....); unsigned* l2pt = l1pt[hi] &~ ((1 << 12) - 1); if (l2pt == FAULT) return FAULT; return l2pt[med] &~ ((1 << 12) - 1); }
The hardware traps when given a "bad" address. (There's no physical memory in the page table). The kernel takes over, and it can:
unsigned long long (unsigned va) { return swapbase(va >> 12); }
Say we have a page table, which points to a bunch of processes in physical memory.
Swap space---------------------------------------------------- | | 1 | | 3 | 2 | | 5 | | 4 | |(virtual mem)| --------------------------------------------------------------
Physical memory is a cache of swap space.
malloc(large_array)