Overview of the Nachos Operating System

As provided to you, Nachos Nachos includes a simple operating system for the simulated workstation. The operating system is capable of running small user programs on the workstation. Since paging is not implemented, running programs must fit entirely within the (small) physical memory of the simulated machine.

Normally, an operating system runs on the machine that it manages. Nachos is unusual in that the operating system runs "side-by-side" with the simulated machine that it controls. The program nachos implements both the machine simulation and the operating system. Understanding this "side-by-side" relationship between the simulated workstation and the operating system is critical to understanding Nachos.

Although side-by-side execution is rather unusual (and unrealistic), it has some important advantages. Most importantly, since nachos is just a UNIX C++ program, you can use all of the usual program development tools while you are working on the operating system code. In particular, you can, and should, use debuggers such as gdb to locate problems in your code.

When you are getting started with Nachos, your goal should be to understand how a user program gets loaded and executed, and to understand what happens when a running user program makes a system call to request a service from the Nachos operating system.

User Program Execution

The -x command line argument to nachos can be used to make the Nachos operating system execute a specified user program, once it has initialized itself. Once an initial program is running, it can then launch additional program executions using the Exec system call.

To cause a user program to be executed, the Nachos operating system must do several things. It must load the user program's machine language instructions and data into the simulated workstation's memory. It must load initial values into the important registers of the workstation's CPU. For example, it must load the memory address of the program's first instruction into the program counter. Once the simulated memory and registers have been set up, the operating system then simply orders the simulated machine to run, via a call to the method Machine::Run. At that point, the machine simulation begins running the loaded program. It does this by simulating the fetch/execution cycle of a real MIPS processor. Essentially, the processor fetches the next program instruction from the (simulated) memory, decodes and executes it (which may change values in the simulated registers or memory), increments the program counter, and then repeats.

An important thing to understand is that once the operating system calls Machine::Run, operating system code is no longer running; the machine simulation is. The call to Machine::Run never returns. The operating system has lost control while the simulated machine executes the user program.

Interrupts and Exceptions

On a real machine, there needs to be a mechanism for returning control from a running user program to the operating system. Similarly, in Nachos, there needs to be a mechanism to return control to the operating system from the machine simulation. As on a real machine, this is accomplished in Nachos by interrupts and exceptions. An exception occurs when the running user program performs a system call, or when it attempts to perform an illegal operation (such as dividing by zero, or accessing a prohibited part of the simulated machine's memory). When an exception occurs, the machine simulation calls an exception handling function, which is part of the operating system. The Nachos exception handling function is in the file code/userprog/exception.cc.

When the exception handler is called, the operating system has control once again. Normally, its job is to figure out why the exception occurred, and to take some action to resolve it. If the exception was caused by a system call, the operating system performs the service requested by the user program, e.g., writing a character to the console, or opening a file. If the program performed an illegal operation, the operating system must decide what to do, e.g., it may simply terminate a user program that tries to divide by zero.

The operating system indicates that it has finished handling the exception by returning from its exception handling function. This returns control to the machine simulation, which once again begins fetching and executing user program instructions.

Interrupts are very similar to exceptions. However, they are not caused by instructions executed by the running user program. Rather, interrupts are causes by the other hardware components of the simulated machine: the disk, the console, the network interface, and the timer. For example, the simulated console generates an interrupt whenever a new input character is available from the keyboard. Each time it finishes executing a user program instruction, the hardware simulation checks whether one of these devices is ready to cause an interrupt. If so, the hardware simulation invokes an operating system interrupt handling function instead of executing the next user program instruction.

Like an exception handler, an interrupt handler is an operating system function. When the interrupt handler is finished, it returns. This returns control to the hardware simulation, which then proceeds with the execution of the next user program instruction.

There are several types of interrupts: one for the disk, one for the timer, and two each for the console and the network. For each type of interrupt, the operating system must provide a separate interrupt handler. The hardware simulation calls the appropriate handler when an interrupt of a particular type occurs.

Operating System Facilities

In addition to the code for loading and executing an initial user program, the initial Nachos operating system includes several other facilities: a thread system, some support for the console, two file system implementations, and a "post office" facility to support network communication between independent Nachos machines. These facilities are used to implement the various system calls that the operating system needs to handle. For example, when a file-related system call is made, the exception handler handles that system call using the file system facility.

Nachos Threads

Nachos includes a thread package (in the directory code/threads, which allows you to create new threads, switch control from thread to thread, and terminate threads. Thread synchronization facilities such as semaphores, locks, and condition variables are also provided. nachos is a concurrent program.

Nachos File Systems

As provided to you, Nachos actually contains two different file system implementations, a "stub" implementation and a "real" implementation. Only one of these two implementations is actually compiled into nachos and used. As provided to you, the nachos program will be built using the "stub" file system. You switch to the "real" file system by setting a flag in the Nachos makefile and rebuilding the program.

Both file systems provide (nearly) identical interfaces, which allow the operating system to create and destroy files, and to read and write data from files. The interface itself, and the two implementations, can be found in the code/filesystem directory.

The "stub" file system is implemented by translating each interface call into UNIX file system calls. Thus, when the operating system creates a file called "foo", this will be implemented by creating a UNIX file "foo", which will appear in the directory in which you are running nachos. This "stub" implementation is unrealistic, since it is reaching outside of the simulation to implement files. However, it is a very simple and convenient initial way to provide file services to be used by the operating system. For example, when the operating system wants to execute a user program, it must load the program's code and data into the simulated machine from a file. If the "stub" file system is being used, this file can be a regular UNIX file (which you will already have created in the code/test directory).

The "real" file system is implemented using the simulated workstation disk. That is, all files are stored on the simulated disk. Thus, requests to read or write file data will take some simulated time, unlike requests to the "stub" file system.

Nachos provides several command line arguments that can be used to initialize and control the "real" file system, when it is being used. The format argument (-f) can be used to tell Nachos to format the simulated disk during initialization. This creates a new, empty file system on the disk, destroying any existing file system that might have been stored there. Another argument, -cp, allows you to copy a UNIX file into the Nachos file system. This is the only way to get "outside" (i.e., UNIX) files into the Nachos file system so that they can be read and written by the Nachos operating system. For example, when using the "real" file system, it is necessary to copy executable files into the Nachos file system using -cp before they can be executed, since Nachos will otherwise have no way to access them.

Nachos Post Office

The Nachos post office is a facility for exchanging messages between numbered mailboxes on different Nachos workstations. Using the post office it is possible to send a messages to a specific mailbox on a specific machine, or to receive a message from a specified mailbox. As provided to you, the post office facility has several limitations: messages have a fixed maximum size, and message transmission is not reliable.

The post office facility is implemented using the underlying simulated Nachos network. The implementation can be found in the code/network directory.