Makefile: /blugreen/homes/plank/cs560/labs/lab7/Makefile
Lab 7 -- Virtual Memory
Below we describe the JOS interface to virtual memory.
Your job is get virtual memory
and demand paging working in JOS. When you are done, everything
should work as in lab 6, however you can have an arbitrary number
of processes in memory, and the processes should use the memory they need,
rather than a stock 135K.
Compiling Information
Use the Makefile in the lab7 directory to link your code with:
- /home/cs560/libsim/sol2/main_lab4.o.
- /home/cs560/libsim/sol2/libsim.a
- An updated version of simulator.h
- The usual
Libfdr
routines
Also, in order to get JOS to run, you have to create
your disk (this is used in lab 8, but you have to make it here).
To do this, execute the file
/home/cs560/bin/sparc-sun-solaris2.9/makedisk.
This will create an 8
megabyte "disk" file somewhere that you will have sole access to.
Once you make some adjustments to your code involving User_Base
and User_Limit you will see the following when you fire up JOS:
JOS booting... done.
Probing disk... done.
Probing console... done.
Once you have created a disk, you should not run makedisk
again. If you get any JOS errors about the disk send
the TA's the errors right away so that they can track the problem
down.
Lab Description
Virtual Memory
The main_memory array will still exist as the user-accessible
memory. However, the User_base and User_limit registers
go away. They are replaced by page tables. Each process should have
a page table, defined as follows:
typedef struct {
PTE **table;
int tableSize;
} PageTable;
typedef struct {
int physicalFrame;
bool valid;
bool dirty;
bool use;
} PTE;
These are defined for you in simulator.h
and the simulator understands them, so you are not free to change them.
Pages are PageSize bytes (defined in simulator.h).
I would recommend having processes allocate page tables with enough
entries for user processes to be 8 megabytes in size.
Run_user_code has been changed so that it takes
a set of registers and a page table:
run_user_code(int registers[], PageTable *pageTable);
Whenever a memory location is accessed in the user's program, the page
table is consulted to find what the physical address of that memory location
is. If the valid field is set, then the address is at the
page denoted by physicalFrame. Note that this is a page number.
In other words, if physicalFrame is i, then the page
begins at main_memory+i*PageSize.
If the valid field is not set, then JOS will
get control in exceptionHandler(), with the which
argument set to PageFaultException, and with the
faulting address in register BadVAddrReg
(see simulator.h). If the page is not legal (see lazy
sbrk() and stack allocation below),
this means you should terminate the program
(seg fault).
If you run out of memory, you must suspend the process asking
for memory until another process frees some or dies. This
requires a little care in your implementation.
Can this deadlock the OS? Yes!
Can you detect it? Yes!! Should you deal with this and fix
it? Yes!!!!!!! How? By selecting a victim process and killing
it. Here's how you should select a victim -- memory is allocated
in the following places, so here is how you should deal with
victims:
- fork() -- kill the child if it exists yet.
Kill the parent if needed.
- exec() (setting up the stack) - kill caller
- sbrk() kill the caller
- Page fault: kill the faulting process.
Other changes
The following procedures have been added or modified to work with
page tables:
- size_user_program(char *filename) returns the initial size
of a program (i.e. the size of its code/gloabls)
without loading it into memory
(-1 if an error occurs).
You should use this function make sure that your page table has enough
valid entries in it to load programs.
- load_user_program(char *filename, PageTable * pageTable) does
the loading of a user program. The page table is assumed to contain enough
valid page table entries to the point where the code and globals of
the program can be loaded into memory. Results are undefined if an
invalid page
table entry is accessed. Loading a program does not reference any page
outside the code and data. Thus you have to initialize (zero out) the
stack and/or mark the heap as invalid.
With paged virtual memory you must be aware that logically contiguous data
may not be contiguous in physical memory. For example, a buffer on your
stack might span separate, non-contiguous physical pages.
You will need to deal with this
issue in system calls such as read(), write(), pipe(), and
execve() -- anything that copies data into or out of process memory
other than through a register. Ioctl(), and fstat() are also
included in this
group but we have the following functions that will correctly write data
to memory using a page table.
- ioctl_console_fill(PageTable *pageTable, int vaddr)
- stat_buf_fill(PageTable *pageTable, int vaddr, int blk_size)
As with the load function, these functions expect that the pages referenced
are valid. Results are undefined if a referenced page is not valid.
Lazy sbrk() and stack allocation
You should implement sbrk() in a lazy fashion: sbrk()
should simply mark somewhere (in the page table will be a good
place) that the page is ok, but set the valid bit to zero.
Then grab a page frame for the pages when they page fault.
Treat the stack in this manner too -- i.e. only allocate as many
frames as you need, and let the stack grow by page faulting. You
may assume that any page fault within 10K bytes of the current
top of the stack is a legal memory reference in the stack.
This means that the following page faults should turn into segmentation
violations:
- Negative addresses.
- Addresses bigger than VM
- Any address between the sbrk() value and the
top of the stack minus 10K.
Yes, this can break if you try to allocate more than 10K of locals, but
so be it.
No ``WHAT_DR_PLANK_DID'' file
You're on your own to design the solution to this lab. Even your Aunt
Heloise wants you to ride your own two-wheeler without training wheels
on this one. She does have a couple of suggestions.
- Start with the argtest program and not with jsh right
away. Think about what you are going to do to write the argv[]
and argc values out to the stack of the program. Shouldn't the pages
be valid? How many will you need?
- Now do real paging. Only allocate
however many pages the process needs. Make fork(), execve
and sbrk() work correctly. Test again. When a process touches
a page, you will catch a page fault through the exception handler.
Handle the exception.