Discussion Section: Wednesday,
April 21, 1999
Getting Started with Lab 4
Implement a memory management scheme that does
paging in addrspace.(h/cc)
-
each process should have its own VIRTUAL address space
-
every process will have a virtual address space starting at address zero
-
these virtual spaces will eventually be mapped into unique physical memory
addresses by code you write
-
we need to associate the address space with a process via the process ID
Concept Here:
-
currently, nachos thinks only a single user process is running and it gives
the entire memory space to it
-
implement a memory page map table
-
change the methods for manipulating the address space to reflect the fact
that a process has access to only a subset of physical memory
-
to do this, change the addrspace constructor to initialize the page table
by looking for free page frames; currently it assumes they are all free
use pageManager->Find();
What to do:
-
modify the AddrSpace constructor to include an ID as an input parameter
-
call it ProcessID
-
you may find it helpful to create a new member function getID() which returns
this ProcessID
-
when you create a new address space via 'new AddrSpace()' you will pass
in the process ID as well as the executable
-
associate the address space with the process information
-
load a process's entire address space into physical memory when the process
is started
-
do not need to implement demand paging
-
remember to free the memory when the process exits
-
output a DEBUG message for each page loaded into memory identifying the
virtual page number and the physical page frame
-
in the constructor for AddrSpace you must get a base physical address where
the application will be placed
-
this is functionality you put into the global memory map table
-
each address space has its own pageTable that represents the VIRTUAL address
space
-
your job is to assign the proper physical page index to each of the virtual
pages
-
you can test if there is enough free memory by writing a test using pageManager->NumClear
Modify the address space code appropriately:
-
detect if memory is not allocated and supply a test
routine to check for success or not
-
you cannot return a success value when doing 'new
AddrSpace()'
-
you need an explicit test for success before using
the address space
-
you can no longer zero out all of physical memory,
only the process' portion
-
you can make a simple function that translates a
virtual address to its physical address and use it wherever virtual addresses
are being used
Phyaddr = Virtaddr + base*PageSize;
-
add checks to verify that the user process does not
reference physical memory it does not own
-
in destructor, if memory has been allocated return it to the global memory
map pool for another process to use
-
you also must write and modify the two functions AddrSpace::SaveState()
and AddrSpace::RestoreState()
-
I'll leave those to you to figure out
Notice: the method RestoreState().
This method is called by scheduler.cc method Scheduler::Run() which sets
the current pageTable to be that of the thread (process) swapped in. Thus,
you don't have to worry about updating the page table to reflect which
process you are running. This is a gift, but be aware that it happens.
Globally Speaking:
-
you will need to modify what you've done to handle the retrieving open
files in the multitasking environment
-
you should have a list of processes and each process should have its own
open file list (as per last assignment)
-
you must modify your code from the last assignment accordingly
Implement an object to keep track of free pages:
a page manager
-
instance of BitMap (from bitmap.h)
This should take the form of:
BitMap *pageManager
which you will have to declare and create a new instance of in system.cc
(add anything you feel you need to add in system.h as well)
don't forget to delete it also in Cleanup()
bitmap must be updated whenever a process starts or stops
Now the Syscall functions
SyscallExit() ... what
a user process calls to die
-
a process calls SyscallExit() passing in one
int argument (process status) indicating it's return status
-
this function releases the address space of the process and joins (calling
SyscallJoin) on any children of the process that were never joined on (thus
freeing up 'zombies')
-
all open files are closed (as before)
-
function then puts the exit status in processPtr->status
-
semaphore is used to prevent the parent from joining before this function
is called
-
then the thread dies gracefully using currentThread->Finish()
-
if you implemented SyscallExit() as a call to SyscallHalt() in the last
lab, you need to change this
-
to do this, have the thread call Thread::Finish() instead of SyscallHalt()
as you likely did when only a single process was running
NOTE:
If you did not break your syscalls into functions in the last lab, you
must do so in this lab. One long ExceptionHandler function is unacceptable
coding style. ExceptionHandler should call SyscallExit, SyscallJoin,
and SyscallExec as well as all of the other Syscall Functions. You
WILL
loose points if you implement one large ExceptionHandler
function again.
Read the values out of machine->readRegister(4),
machine->readRegister(5), machine->readRegister(6) and send them as parameters
to the functions.
SyscallExec()
forks and executes a new program in a new address space
-
it starts out by converting name to a string in kernel memory if the thread
that called exec is in userspace
-
a unique ProcessID is then obtained
-
opens the executable file and sends it to a new address space
AddrSpace *aspace = new AddrSpace(...pass appropriate arguments here...)
-
if any check fails, for instance if the file doesn't exist, or there is
not enough memory, -1 is returned
-
the new process is inserted into a list of children for the parent (this
is used later in SyscallJoin and SyscallExit)
-
then a new process entry is made in the linked list of processes
-
the data in this entry is initialized
-
finally a new thread is made and the process can execute via a call to
Fork() passing as one of its parameters "ExecProcess" (see the lab handout
for more details on that)
-
the ProcessID is returned to the calling function
Note: the initializing registers and
state should be done after forking off the child thread
Note:
-
test every syscall
-
people lost points last assignment for not testing if things were NULL
Openfile *file = fileSystem->Open(kernelName);
if (file == NULL) ..... deal with it appropriatly
or you can test
if (file != NULL) .... continue doing things
and then have an else statement to deal with the contrary
SyscallJoin()
-
used to join and retrieve the exit value of a processes child
-
the first thing it does is go through the calling process's child list
to ensure that the child on which the calling process wishes to join is
indeed it's child
-
if it isn't, -1 is returned
-
the child is then removed from the childlist
-
the next thing that occurs is the process entry of the child is found and
a semaphore is used to make sure that the status variable is set to the
child's exit value before using it
-
in other words, the semaphore is used to make sure the child actually exited
-
when this is true, the value is taken from status
-
then the child's process entry is deleted and this function returns the
status