Course Outline
Paper
Nachos Project
Emacs
RCS
Primary: Nachos Project (70% of grade; 90% of work)
Secondary: Summary Paper (30% of grade)
Suggested reading list on the Documents page.
You may choose from these papers. Other papers are also acceptable upon approval.
Developed originally at U.C., Berkeley.
Nachos = N(ot) A(nother) C(ompletely) H(euristic) O(perating) S(ystem)
This name is reminiscent of the name of a Unix utility: YACC, which stands for Yet Another Compiler Compiler, a "compiler" whose output is another compiler. (Or can be viewed that way.)
Written in C++ using the GNU g++ compiler, and set up to run on various Unix platforms.
Simulates the entire instruction set for the Mips processor used in Silicon Graphics machines.
Simulates the hardware of Mips machine
Written in C++. You will not modify this code.
Simulates:
Implements Nachos "kernel" processes (called threads).
This code is written in C++, is compiled on the actual hardware (Sun machine, running under Unix Solaris).
A Nachos kernel thread executes some Nachos C++ function in the Nachos code.
Nachos code can already do context-switches between these kernel threads.
Does not use Solaris threads.
Kernel threads (like all the Nachos additional code you will write) runs on the actual hardware.
Kernel threads are not subject to the Mips interrupts, but can yield to another ready kernel thread.
A context switch between these kernel threads requires saving the register values on the actual machine for the first thread and restoring the machine register values for the second thread into the actual hardware registers.
The context switch between Nachos kernel threads therefore requires a data structure (one for each kernel thread) for storing the actual hardware register values.
The Nachos code contains a C++ class, Thread to represent both Nachos kernel threads and Nachos user threads. (More about user threads below.)
Since saving and restoring the actual hardware registers cannot be done directly in C++, the Nachos code contains some assembler code for doing the part of the context switch that saves and restores registers.
The context switch code needs pointers to two Thread objects, the old thread and the new Thread.
Note that the old thread is the one that is running when the switch routine is invoked and continues to run up until the point that the register values for the new Thread are restored into the actual hardware registers.
Nachos is the operating system code for the simulated Mips machine.
So the purpose of Nachos is to execute user programs on the Mips machine.
This means that user programs must be compiled and linked into Mips executable code.
In order to execute a file containing Mips executable code, it must first be loaded into the Mips memory.
The Mips machine, simulated in C++ code, simulates memory as an array whose size determines the amount of Mips "physical" main memory.
The initial Nachos code (before your additions) allows a single user program to be executed at a time.
However, there are severe limitations with this initial code.
Note that this address 0 is the offset into the array which simulates the Mips memory. It is not an address in the Nachos executable image.
Why? Because, like almost any user program, these user programs must make requests of the operating system (Nachos) for I/O and for terminating (Exit).
The inital Nachos code gives up when any system call is made except for the Halt system call.
The compiled/linked user programs do attempt to make system calls. The generated Mips code contains the Mips instruction: syscall ...
void ExceptionHandler(ExceptionType which);
This action is part of the hardware simulation.
You will need to modify this function to extend the functionality of Nachos.
First, note that the ExceptionType passed to this function could indicate a "problem" exception such as OverflowException or IllegalInstrException.
Or, ExceptionType can indicate a "normal" event that needs Nachos operatings system attention or assistance such as PageFaultException or SysCallException.
If the parameter passed to ExceptionHandler has the value SysCallException, this simply indicates that the user program has made some system call, but which one?
The user Mips code that invokes syscall, first loads Mips register 2 with the integer that identifies the particular system call being invoked. Then it executes the syscall instruction. E.g., in Mips assembler:
Halt:
addiu $2,$0,SC_Halt
syscall
No.
So the ExceptionHandler routine must read Mips register 2. The simulated machine contains routines for accessing such information. E.g, the Nachos code has a gobal variable, machine, which is a pointer to the single instance of the C++ Machine class. To read register 2, the code is:
int type = machine->ReadRegister(2);
Here is the initial code for ExceptionHandler:
void ExceptionHandler(ExceptionType which)
{
int type = machine->ReadRegister(2);
if ((which == SyscallException) && (type == SC_Halt)) {
DEBUG('k', "\nShutdown, initiated by user program.\n");
interrupt->Halt();
} else {
printf("Unexpected user mode exception %d %d\n", which, type);
ASSERT(FALSE);
}
}
As you can see, every user system call except for Halt falls into the else clause and executes the ASSERT(FALSE) statement which terminates Nachos.
The other system calls (not implemented in the initial Nachos code, except for SC_Halt) are specified in the header file, syscall.h:
#define SC_Halt 0 #define SC_Exit 1 #define SC_Exec 2 #define SC_Join 3 #define SC_Create 4 #define SC_Open 5 #define SC_Read 6 #define SC_Write 7 #define SC_Close 8 #define SC_Fork 9 #define SC_Yield 10
The initial Nachos Code has data structures for Page Tables.
As noted above, the initial Nachos Code also allows you to execute user programs, at least up to the point that the user code makes a Nachos system call.
Page tables provide a mapping from each virtual page to a physical page for user programs.
If partial loading of programs is in effect, the page table also indicates whether a given virtual page in a user program is present in the physical memory.
The initial Nachos code shows you how to read a Mips executable file and load the different parts into the correct places in the Mips memory.
The "parts" are the segments. Each Mips executable can have:
The Mips executable file contains a header record (the first 40 bytes or so) which describes the segments.
Each segment description says at what virtual address that segment expects to be loaded.
This segment information is used by the initial Nachos code to load the executable program from a file into the Mips memory.
The initial Nachos code cheats, however, as loads each virtual page at the same Mips physical page. That is, in the initial Nachos code, virtual addresses are always the same as Mips physical addresses.
Don't worry about this for now. Several system calls can be implemented without worrying about virtual memory.
After a brief introduction to some of the features of the initial Nachos code, what are you supposed to add? What functionality is to be added?
A unifying goal is to add enough functionality to Nachos so that it is possible to write a shell as a Mips user program. This shell would then be able to read commands (names of Mips user programs) from a command line, create a Nachos process to execute the command, wait for the user program to terminate, print a prompt and wait for the next command. As an extension, we could also arrange to execute programs in the background (by not waiting for the program to finish).
What functionality needs to be added?
Since the goal is to write a shell as a Mips user program, how do we create and compile Mips user programs?
Answer: A cross compiler is provided built from GNU's gcc compiler. This cross compiler runs on the Sun machine, but produces machine code for the Mips processor.
So basically you can write C programs. These programs include the appropriate header for Nachos system calls.
Compile the C programs with the cross compiler.
Emacs
Etags
RCS