CPSC 331 Operating Systems Fall 2005

Nachos Practicalities

warning Additional material will be added as it becomes relevant.
warning Some updates have been made in the Nachos directory structure, and the "Setup" and "Finding Your Way Around" sections have been updated accordingly.


Nachos is found in the course directory /classes/f05/cs331/. The subdirectory nachos contains the Nachos source code and should be copied to your own directory before starting the first Nachos assignment. The bin, lib, and decstation-ultrix subdirectories contain utility programs necessary for creating user programs to run under Nachos. Do not copy this directory.

Since Nachos projects are group projects, how you manage your working directories is up to you - but you'll probably want to have one directory where the most recently working version of the project is stored. For this, choose one team member's account to host the Nachos code. To grant the other team member access to the directory, do:

fs setacl dirname username rwldi

where "dirname" is replaced by the name of the Nachos directory (e.g. ~/cs331/nachos) and "username" is replaced by the username of the other team member. rwldi is the set of permissions being granted: read, write, list, delete, insert (i.e. create new files).

Check that all of the Nachos subdirectories have the correct permissions with

fs listacl dirname username rwldi

and update them as needed.

With multiple people working with the same set of files, the chance of accidental deletions goes up. Be careful, and make backups often!

Finding Your Way Around

The Nachos package in the nachos directory has several main subdirectories: build, code, userprog, patches, and precompiled. Some of the directories may not be present in the initial distribution but will be available as they are needed later in the semester.

The build directory is where all of the compiled object files and the Nachos executable reside. In the initial distribution, you'll find a Makefile. Look at the comments in the Makefile or see below ("Compiling and Running Nachos") for information on how to compile Nachos.

The Nachos source code is found in the code directory, which is organized into several subdirectories:

You may add to and modify files in any of these directories except the machine directory - developers can't usually make changes to the hardware, so you can't either!

The userprog directory contains sample user programs, along with a Makefile for compiling them.

The patches directory contains diff files needed to "upgrade" the provided code for various projects, and the precompiled directory contains compiled solutions for earlier projects so that you can work on later projects without having working code.

As you sift through the Nachos source code, the grep and find commands can be very useful for finding files, function calls, method implementations, or other things which you know the name of but can't locate. (In particular, method implementations aren't always where you might expect - e.g. Machine::Run() is in mipssim.cc even though the Machine class is defined in machine.h.) See the "Useful Commands" section below or look them up in the Linux manual pages.

Compiling and Running Nachos

The instructions for compiling Nachos can be found in the Makefile in the nachos/build directory. They are summarized below. Since Nachos consists of many files, you will be using make to automate the compilation process.

Compilation always takes place in the build directory - all of the compiled object files and the final nachos executable will end up there, not in the source code directories.

Compiling Nachos for the First Time

  1. Make sure you are in the build directory of the Nachos distribution.
  2. Type make depend. This computes file dependencies (i.e. what files are required for each .o file generated) and stores them in the Makefile.dep file in the build directory. This information is used by g++ to avoid recompiling .o files whose sources have not changed.
  3. Type make. This compiles Nachos and produces the executable nachos.

You may get the following compiler warning:

../code/threads/synch.cc: In member function `bool Lock::IsHeldByCurrentThread()':
../code/threads/synch.cc:196: warning: control reaches end of non-void function

It is safe to ignore this for now - it should go away once you complete the implementation of the Lock class in the first Nachos assignment.

Try running Nachos: nachos -u will print out some information about Nachos' commandline arguments, or you can run nachos -T 1 to run the built-in thread test - you should see output about threads 0 and 1 looping.

Recompiling Nachos After Changes

If you have only changed C++ code in existing Nachos files, then

  1. Make sure you are in the build directory of the Nachos distribution.
  2. Type make.

If you have added any new files (.cc or .h files) or added or deleted #includes in any of the existing files, then

  1. Make sure you are in the build directory of the Nachos distribution.
  2. Type make depend.
  3. Type make.

The make depend step is important for updating dependencies if you have done something that might cause them to change. It is always safe to do make depend even if you have only changed existing C++ code; it just shortens the compilation process if you only do it when necessary.

Important Note: If make is behaving strangely or you run Nachos and it doesn't seem like the changes you just made have taken effect, do the following:

  1. Make sure you are in the build directory of the Nachos distribution.
  2. Type make clean. These removes all of the .o files generated by the compilation process so that the next make will recompile everything from scratch.
  3. Type make depend.
  4. Type make. Compilation will take a bit longer since everything must be recompiled, but you'll be working with a clean slate.

Compiling Within Emacs

One of the many nice features of emacs is that you can compile within the editor, and then can be taken to the exact line where a compiler error occurred.

To compile:

  1. Save all of the currently-open files.
  2. Make sure that the current buffer contains a Nachos source file.
  3. Type M-x compile. M-x stands for meta-x, and is achieved on PC keyboards by pressing and releasing the Esc key, followed by pressing and releasing the x key. (Some keyboards actually have a key labelled "meta".)
  4. emacs will now prompt you with compile command: down in the "minibuffer" at the bottom of the emacs window. If it doesn't already say make after "compile command:", edit it so that it does. Then press enter. This tells emacs to carry out the make command in the current directory. (To make this work, a minimal Makefile has been provided in each of the code subdirectories.) Your current buffer window will be split in half and the output from make will be displayed.

Note that if you want to execute make depend or make clean, you'll need to switch to the build directory and run these from your terminal window (see above).

To locate and fix compiler errors:

  1. Wait for the compilation process to complete.
  2. Go to the first error (if any) by pressing C-x ` (that's control and x simultaneously, then the backquote). emacs will automatically load the correct file and place the cursor on the line in question.
  3. Fix the bug.
  4. Press C-x ` again to go to the next error.

If you want to really automate the process, you can set up emacs so that a single keypress (I use the insert key) will prompt you to save any unsaved files and will then invoke make. (No more confusion because you forgot to save something before compiling.) To do this, paste the following at the end of your ~/.emacs file and restart emacs:

;; define a function to save buffers and then compile
(defun save-and-compile () "save and make" (interactive)
  (compile "make")
  (other-window 1)
  (end-of-buffer ())
;  (goto-char (point-max))
  (other-window 1)
;; bind save-and-compile to insert
(global-set-key [kp0] 'save-and-compile)
(global-set-key [insert] 'save-and-compile)

Then just make sure the current buffer displays a Nachos source code file, and press insert. Ask about making these changes if you've never fiddled with your ~/.emacs file (or don't know what one is), or if you want to use a key other than insert.

Writing, Compiling, and Running User Programs

Writing User Programs

Writing a user program is mostly just a matter of writing a C program - Nachos can, in theory, run any C program. The wrinkles: the system calls and library functions supported by Nachos, the fact that you're working in C and not C++, and the limitations of cross-compiler used to compile the programs.

System calls and library functions:
User programs, since they run under Nachos, can only use the system calls supported by Nachos. Furthermore, they cannot use many of the common Unix libraries. Nachos' system calls are a pretty limited set, and you might be surprised at how many useful things are system calls or library routines. (Things like printf and scanf are particularly frequently used.) A very primitive I/O library is provided in userprog/iolib.h to make some basic I/O operations like printing a string or integer or reading a line of text easier. (You can look at the implementation in userprog/iolib.c to see how they are implemented - they are just wrappers around the Read() and Write() system calls.) However, it is still very annoying to write a very complex user program. Feel free to expand the I/O library or build other libraries to help with common tasks.

C, not C++
One of the biggest things is that C doesn't have classes. So, C doesn't have a string class. (C-strings are arrays of characters.)

Cross-compiler limitations:
Not only are you limited to C, you are limited to a subset of C. Amongst the things that you might want to do are such diverse elements as:

Compiling User Programs

Compiling a Nachos user program is not done the same way as compiling Nachos itself (or any other program you might write). Two reasons:

The good news is that there is a Makefile in the userprog directory which handles all of this for you. You will just need to tell the Makefile about your program:

Once you've modified the Makefile, use make to compile and see the section below for information on how to run your program. Note: for some reason, it doesn't work to compile the user programs on the Lansing lab machines (you'll get Internal compiler error: program cpp got fatal signal 11). However, if you ssh to math.hws.edu and compile there, it works. Please only use math.hws.edu for compiling user programs - do the rest of your work on a lab machine (or some other machine) so math.hws.edu doesn't get bogged down.

Running User Programs

The first user program to start up is specified via the -x option to Nachos. Following the -x is the complete pathname to the executable to run. For example, if you are in your build directory (where the Nachos executable is) and want to run the userprog/halt program, use:

 nachos -x ../userprog/halt

You can use any pathname to specify the location of the program you want Nachos to run, including absolute paths. This example assumes that your Nachos directories are set up in the standard way.


Builtin Messages

Nachos is capable of displaying some debugging information. This can be turned on by running Nachos with the -d option and specifying what categories of debugging messages you want. The choices (defined in lib/debug.h) are:

+turn on all debug messages
ssynchronization (semaphores)
mmachine emulation
ddisk emulation
ffile system
aaddress spaces
csystem calls
nnetwork emulation
uuser-defined messages

For example, to run with the builtin thread and interrupt debugging messages, use

nachos -d ti

Adding Your Own Debugging Messages

You can print debugging messages using cout anywhere in the Nachos kernel code, but remember to remove all such output before handing in your program!

You can also use Nachos' builtin debugging message mechanism to print your debugging messages. The advantage to this approach is that you can run with and without the debugging output, just by specifying different commandline options. If you use this approach, you do not need to remove your debugging messages before handin. (It might be useful to have them in place later, for example.) To print a debugging message, use

DEBUG(<flag>,<what to print>);

For example,

DEBUG(dbgUser,"x is " << x);

would result in cout << "x is " << x occurring if the debug flag dbgUser was specified when Nachos started. Note that dbgUser is a constant defined in lib/debug.h; the corresponding flag that would be specified on the command line is 'u'.

You should use dbgSynch for messages in the Lock and Condition classes which would be useful in general debugging situations. (See the Semaphore class for an example of what sorts of messages this might include.) Use dbgUser for all other messages.


A mechanism has been set up for running kernel tests. To run a tester, start Nachos with the -T <#> option e.g.

nachos -T 1

will run test #1 (the thread tester). The basic tests are:

0library functions

These tests aren't especially comprehensive, but they do test some basic functionality.

To add your own tester, you should do the following:

See the provided testers for examples.

Useful Commands


grep searches files for occurrences of a given pattern. The basic usage is

grep <pattern> <file(s)>

where <pattern> replaced by the pattern you are looking for and <file(s)> is replaced by a list of filenames to search.

For example,

grep Run *

will search all files in the current directory (* is a wildcard which matches all files in the current directory) for the string "Run", and prints the matching lines along with the name of the matching file. grep is case-sensitive, so use

grep -i Run *

if you want to find all occurrences of Run, run, rUn, etc. You can specify several sets of files to search e.g.

grep Run machine.cc mipssim.cc ../lib/*.h

to search machine.cc, mipssim.cc, and all header files (ending with .h) in the directory specified by the relative path ../lib.


find searches for files or directories with a particular name. The basic usage is

find <dir> -name "<pattern>" -print

where <dir> is replaced by the name of the directory where the search begins and <pattern> is replaced by the pattern specifying the files/directories you are looking for.

For example,

find . -name "*.cc" -print

finds all files/directories whose names end with .cc, searching the current directory (.) and its subdirectories. To find all files/directories with names starting with s in ../lib and its subdirectories, use

find ../lib -name "s*" -print

If you want to find a particular file, there's no need for the wildcard in the pattern:

find . -name "machine.cc" -print

C-s and C-r

In emacs, you can search forward from the cursor position with C-s (control + s) and backward with C-r. After pressing C-s or C-r, begin to type the string you want to search for. (It will appear in the "minibuffer" at the bottom of the emacs window.) If you want to search for additional occurrences, press C-s or C-r more than once. To stop searching and have the cursor remain at the location where the thing was found, you can press an arrow key or C-a (the keyboard shortcut for putting the cursor at the beginning of the current line). To cancel the search and return the cursor to where it was when you started the search, press C-g (the universal quit-an-emacs-command command).

Valid HTML 4.01!