WARNING: Doing what is outlined here may cause your current implementation of assignment 2 and 3 to break if they are not implemented 100% correctly. I only suggest doing this if you are pretty certain that assignment 2 is very solid.
Here is something that one of the students (Andrew Prior, Winter 1994) did for the 3rd assignment that you might want to also consider doing. The idea is to try to more realistically simulate what is/will be happening in the real system (and in later assignments). This description applies to things done in assignment 2 as well as assignment 3.
I'll first try to explain where the system is currently inaccurate and motivate why you might want to improve (slightly) the accuracy of the simulation.
In assignment 2 and 3 the underlying UNIX filesystem is being used. This handles synchronization, blocking/unblocking processes waiting for I/O, etc. for you (i.e., it all goes unnoticed by a Nachos process). When you get to assignment 4 you use a real file system implementation. Meaning that suddenly your threads may be force to synchronize and/or wait for the disk to finish transferring data.
A simple example: only one thread can be reading/writing the disk at one time. Therefore, while the disk is busy other threads will have to wait until it is free. The fact the executing thread can become blocked may significantly impact your solution to various design issues in the system (mostly in realizing what is happening atomically). This means that threads calling your system calls may become blocked in the middle of the execution of the system call.
For example, Open will eventually have to read some data from disk (assuming that data isn't cached in memory). This means that at that point the calling thread is likely to get blocked and another thread will become scheduled and be allowed to execute.
The problem is that this blocking is not currently simulated for assignment 2 or 3 and without realizing this can occur and without the simulator simulating this you can arrive at solutions that will not work correctly in an environment where the filesystem is being more properly simulated.
Andrew's solution was to insert some Yields into the code that is used to call the UNIX filesystem (he did this for ReadAt and WriteAt - thus loosely simulating the possibility of a thread giving up the cpu and another thread being chosen for execution. NOTE: Yields aren't being used in any solution - just as a means of enhancing the simulation :-)
If you've followed me this far - here is what you might do (where I've enclosed the new code in the obvious ifdef's). In code/filesys/openfile.h int ReadAt(char *into, int numBytes, int position) { Lseek(file, position, 0); #ifdef EMULATE_BLOCKING currentThread->Yield(); #endif EMULATE_BLOCKING return ReadPartial(file, into, numBytes); } int WriteAt(char *from, int numBytes, int position) { Lseek(file, position, 0); #ifdef EMULATE_BLOCKING currentThread->Yield(); #endif EMULATE_BLOCKING WriteFile(file, from, numBytes); return numBytes; } Don't worry because Read and Write call ReadAt and WriteAt. ---------------------------------------------------------------------- Likewise you can modify code/filesys/filesys.h as follows: bool Create(char *name, int initialSize) { int fileDescriptor = OpenForWrite(name); #ifdef EMULATE_BLOCKING currentThread->Yield(); #endif EMULATE_BLOCKING if (fileDescriptor == -1) return FALSE; Close(fileDescriptor); return TRUE; } OpenFile* Open(char *name) { int fileDescriptor = OpenForReadWrite(name, FALSE); #ifdef EMULATE_BLOCKING currentThread->Yield(); #endif EMULATE_BLOCKING if (fileDescriptor == -1) return NULL; return new OpenFile(fileDescriptor); } bool Remove(char *name) { #ifdef EMULATE_BLOCKING currentThread->Yield(); #endif EMULATE_BLOCKING return Unlink(name) == 0; }