Virtual Memory
- translation lookaside buffer (TLB)
- cache
- context switch
- discussion about where to go with the course
translation lookaside buffer (TLB)
- process of looking up virtual-to-physical address translation
is called table walking
- table walking done by MMU hardware
- may take up to 4 memory reads (plus the actual access) for each
virtual memory access
- locality makes caching worthwhile
- cache for virtual-to-physical translations is called
translation lookaside buffer (TLB)
TLB implementation
- TLB is a table:
- entries are physical addresses of pages
- indices are concatenation of context and virtual page number
- associative memory takes index and returns entry (if any)
- page descriptors fetched by table walking are stored into TLB,
displacing another entry (LRU?)
- TLB "hits" require a single memory access per virtual memory access
- TLB "misses" require 5 memory accesses per virtual memory access
(with 4KB pages)
Memory Cache
- slow memory is cheaper than fast memory
- can you get the benefits of fast memory without paying much
more than for slow memory?
- yes: have a large, slow main memory and a small, fast cache
- keep frequently-accessed instructions and data in the cache
- problem: frequently-accessed instructions and data change over time
Cache Behavior
- cache sits between CPU and memory
- on a read, if cache has virtual memory location, delivers contents
- on a write:
- if cache has location, store contents and write
them back to memory:
- immediately (write-through cache)
- when the location is ejected from the cache (write-back cache)
- if cache does not have virtual memory location:
- write goes directly to memory, or
- memory location is fetched, and new value is written, then
either write-through or write-back
Cache Organization
- data is cached by lines
- depending on system, line can be anywhere between 4(?) and 128 bytes
- bytes within a line belong to adjacent memory locations, and
are loaded and ejected together
- bytes within a line are addressed by less significant bits of
virtual address
- lines are identified by next more significant bits of virtual address
- each line has a tag, which includes:
- context number,
- the most significant bits of the virtual address
- access permission bits
Cache Access
- virtual address provided simultaneously to MMU and cache
- cache compares tag of matching line number
- if tag matches, data is returned (read) or accepted (write)
- if tag doesn't match, we have a cache miss and MMU completes memory
access:
- on read, cache ejects old line (writing back if modified and write-back
cache) and stores new one when memory provides it
- on write, cache may or may not load new line, ejecting old (possibly
writing back old line)
- all reads/writes to main memory are in for whole lines
context switch
- we may decide to switch contexts, i.e. stop executing code for
one process/thread and start executing code for another
- must:
- save all thread-specific information on stack
- save the stack pointer so we can later restart the thread
- get stack pointer for the suspended thread
- pop thread information from new stack
- in user mode: thread switch
- in supervisory mode: process switch (must change context
register in MMU)
- assembly only (impossible in C)
context switch implementation
- save stack pointer
- save program counter (return address from call to this subroutine)
- save global floating point and flags registers
- save all the register windows: save until
all register windows are on the stack
- do enough restore so window will underflow on next restore
- if switching processes, change MMU context (supervisory only)
- reload floating point, global registers, PC, SP
- restore to load topmost register window
- return to reloaded PC
homework
- due Friday, Nov 20th
- write a thread creation routine,
int create(void f(), char * stack):
create takes a function pointer and an area of memory to be used
for the stack and returns a stack pointer for the new thread
- write a thread switch routine, void ctswitch(int new)
- test using the following program
main.c
#include <unistd.h>
extern int create(void f(), char * stack);
extern void ctswitch();
int t1, t2 = 0;
void f1 () {
while (1) {
printf ("thread 1\n");
sleep (1);
ctswitch (t2);
}
}
void f2 () {
while (1) {
printf ("thread 2\n");
sleep (1);
ctswitch (t1);
}
}
main () {
char stack1 [10000], stack2 [10000];
t1 = create (f1, stack1);
t2 = create (f2, stack2);
ctswitch (t1);
}
Preview of the rest of the course
- at end of book
- what next?
- suggestion: compilers
- parsing (lex, yacc)
- semantic analysis
- code generation
- other suggestions? (please come to class prepared)