Why disable interrupts? The following is mainly for your enrichment, although you will certainly understand the assignment better.
Confused yet? It gets worse. Because the user level program is being simulated, the interrupt mechanism we used for Assignment #3 no longer applies to it. Instead, we use mt_os_tick in mt_os.c to simulate the clock interrupt to the user program when mipsi is simulating it.
Furthermore, mipsi was not designed to run with the interrupt mechanism we used in Assignment #3. As a result, interrupts are disabled upon returning from any minithread call. You don't have to worry about this because remember, every call to the minithreads package must go through the system call mechanism. This is why when you look at mt_syscall (where you have to add your system calls), you will see that we have enabled interrupts just before any minithreads or VM routine is called, and disabled interrupts before returning to mipsi.
In summary, the net effect is that:
syscall(TRAPNUMBER, arg0, arg1, arg2, ...);The simulator uses the TRAPNUMBER to translate the call into an appropriate UNIX syscall or to run a routine in mt_os.
Any application that calls routines in mt_os will go through the MT_ENTRY trap number. Calls to the mt_os look like:
syscall(MT_ENTRY, MT_OS_TRAPNUMBER, arg0, arg1, ...);Valid MT_OS_TRAPNUMBER's can be found in mt_os.h.
So how is this all done? mipsi basically simulates each instruction one at a time. Whenever a trap occurs, mipsi goes and translates the trap and forwards it to the host operating system (in our case this is Ultrix) or for traps into mt_os, mipsi simply runs the appropriate mt_os function. For example, when mipsi is simulating a program, the call to minithread_fork looks like:
syscall(MT_ENTRY, MT_MINITHREAD_FORK, proc, arg);mipsi recognizes the syscall and calls
mt_syscall(MT_MINITHREAD_FORK, proc, arg);mt_syscall recognizes that the syscall is to MT_MINITHREAD_FORK, so it packages up the arguments and calls mt_minithread_fork (see mt_os.c to see how this is implemented).
mt_minithread_fork(proc, arg);mt_minithread_fork then does the necessary magic to get things ready and calls minithread_fork, returning the thread id which is propagated up to the user level. Note that all this is happening inside mipsi. As far as the application is concerned, it just did a normal syscall.
syscall is not a very pleasant interface to program with, so a wrapper for using these is provided in USER/user_minithread.c. Here's an example of the use of the syscall interface through the minithread_fork call:
minithread_t minithread_fork(proc_t proc, arg_t arg) { minithread_t mt; mt = (minithread_t)syscall(MT_ENTRY, MT_MINITHREAD_FORK, proc, arg); return(mt); }This will no doubt confuse some people as there are now two files that implement minithread.h: user_minithread.c and minithread.c. Obviously, the former is only seen by user programs that are to be simulated, and the latter is what is actually used in the kernel to implement the system calls. To simulate a user program that uses the functionality provided by mt_os, compile it using the cross-compiler and link in user_minithread.o.
A convenient idiom for declaring handle types is to introduce a C macro that conditionally compiles one way or another, depending on whether the minithread and synchronization object references are pointers (as in Assignment #3) or handles (as in Assignment #4 user programs). The Makefiles that we've given you do this, by defining the symbol KERN or USER appropriately. (Note we don't use the symbol KERNEL because some of the Ultrix include files we use use this for building the Ultrix kernel). For example, you can use this in your program by saying:
#ifdef KERN typedef struct semaphore* semaphore_t; #else typedef os_handle_t semaphore_t; #endifwhere os_handle_t is a type for your exported handles. (A good type here might be an integer).
Notice that we only have one "process" running, and therefore only have one address space, even though we have multiple threads. This means that you do not have to worry about having different page tables for different threads, and that you should not flush the TLB.
TLB_MAKE_PTE(ppage,flags)is provided in tlb.h to turn the physical page number and the flags into the appropriate form.
ReadBlock(disk-block-number, physical address); WriteBlock(disk-block-number, physical address);These calls read and write to disk from (simulated) physical memory in 4K sized blocks (convenient, since this is the page size used in the VM system). A 16 MB disk is provided for swap space. Unfortunately, since this disk is implemented as an array in memory, your core files will be rather large.