[CS Dept logo]

Com Sci 230

Homeworks for Spring 1998

Nachos Project #3: Due Friday 15 May 1998
User Address Space, System Calls

Copyright information

Last modified: Thu May 21 10:12:09 CDT


Project #3 is due on Wednesday, 13 May, at 7:30 AM. Use the submit-project Project_3 command to hand in your work as you did in Project #2. As before, submit partial results, but make each submission self-contained and complete in terms of demonstration and explanation.

Basic goals

In this project, you implement the features required to run several user programs independently, so that individual user programs do not interfere with one another, nor with the OS. This requires you to provide each user program with its own separate address space, to handle all error exceptions from individual programs without crashing the OS, and to schedule threads associated with user programs on the CPU. You will also provide some basic services to user programs through system calls. You will use some of the data structures involved in virtual memory, but you will not do demand paging yet.

Testing user programs

userprog/nachos -x mipscode runs the executable MIPS code in the file mipscode as a user-level program within Nachos. There are some user-level programs already in the test directory, but most of them will not work at all until you provide appropriate support for user address spaces. Just to see that something works, try userprog/nachos -x test/halt.

To test your usercode support properly, you need to write and compile some of your own test programs. Look at test/Makefile to see how the cross-compiler works, and extend this Makefile to compile your own tests. Notice that test/Makefile is written for the make command, not for gmake. Also notice that the cross-compiler compiles C, not C++. Keep your user programs very simple. Debugging the cross-compiler for complex cases will not be fun.

The mipscode that you invoke with userprog/nachos -x is usually not the real user program that you are interested in. Rather, you will usually have a bootstrap program, run userprog/nachos -x test/bootstrap, and let the bootstrap program invoke one or more interesting user programs with Exec.

General requirements

Don't forget the general requirements for all project work, which were provided in the statement of Project #2.

Special requirements for this assignment

The project tasks

  1. Implement exception handling, including the system calls Halt, Exit, Exec, Join, Create, Open, Read, Write, Close. For now, each exception other than Syscall can just print an error message and Finish the thread that caused it. The system calls that manipulate files can use the native UNIX file system, through the file-manipulation functions defined in code/filesys/filesys.h and code/filesys/filesys.cc. The documentation of Exit, Exec, and Join in syscall.h is a bit ambiguous. We will interpret those functions as follows:

    Exit

    Finish the thread that made the call. If this is the last thread using the address space, deallocate the address space. The Nachos documentation seems to say that we should kill all threads associated with the address space. Until we implement Fork for users, there is only one thread per address space anyway, but for future developments the capability to Exit a single thread appears to give more flexibility.



    Exec(code)

    Create a new thread in a new address space, and let it run the MIPS program in the file code. Return an identifier for the new thread. Notice that the original Nachos returns an identifier for the new address space, rather than for the new thread. My variant file syscall.h-alt1 renames the return type to reflect that we return a thread identifier. Notice that, within the user program, the thread identifier is not a pointer to an actual thread, because the thread pointer only makes sense in the OS kernel address space. I recommend that you keep your own table to translate between identifiers and actual thread pointers. Notice that Exec is almost useless until you do part 2, because the new thread will never get the CPU until the old one Finishes. The Nachos Road Map section on experience with multiprogramming discusses another subtlety in the implementation of Exec.



    Join(threadId)

    Wait until the specified thread has Finished. The original Nachos waits for a whole address space to die, but joining with another thread appears to be more flexible. You need to change the code for Finish, as well as writing code for Join. You are responsible for checking the validity of the threadId. Careless code here can provide another loophole through which user code can crash the OS.

  2. Implement time-sliced multiprogramming. Use address translation through a page table to provide independent address spaces, and use a bitmap to keep track of free vs. busy page frames in (simulated) physical memory. Set timer interrupts to allocate time slices to threads.

  3. Improve the Exec function to pass parameters to the program running in the new thread. You may either simulate the parameter passing done by UNIX, and copy the actual parameters of the call onto the bottom of the new thread's stack, or you may provide a new system call GetParams which the new thread may call, and which returns the parameter values.

  4. Com Sci 330 only. Support multiple threads in a single address space. First, implement the system call Fork, to create a new thread in the same address space as the call. Don't forget to allocate space in the address space for the new thread's stack. Next, implement Yield. This is easy, but not terribly powerful. It makes busy waiting slightly less inefficient, but user threads still have no way to synchronize with one another. Finally, provide locks and condition variables to user programs in the same address space. Try to minimize the amount of kernel code and memory used for user-thread synchronization. Notice that all lock and condition-variable queueing can be done inside the user address space by unprivileged code: only the manipulation of the CPU queue requires kernel intervention.

Good Advice