Transcript Document
Nachos OS
Nachos Tutorial
Image courtesy of Thomas Andersen: http://www.cs.washington.edu/homes/tom/nachos/
• What is Nachos?
– Nachos is instructional software that
allows students to study and modify a
real, working operating system
• How does it work?
• How do I get started?
• Important URLs
Nachos Home page:
http://http.cs.berkeley.edu/~tea/nach
os/
• Nachos Overview
• A paper that comes with the Nachos distribution, by
Tom Anderson at Berkeley. A bit dated, but contains
some good advice.
•
Appendix C The Nachos System
• This pdf file is a description of Nachos written by Tom
Anderson at Berkeley. It is published on-line by Wiley
as an appendix to the text.
• A Road Map Through Nachos
• A local copy of Thomas Narten's explanation of Nachos
from Duke University.
•
SALSA.
• A Nachos tutorial and guide developed at the
University of Massachusetts.
• The Nachos Code.
• Browse the Nachos source code for the current lab
exercise.
•
The Nachos Man page
• Run time options for Nachos.
•
The Nachos Makefiles
• A brief introduction to Nachos' makefiles from CMP
111 at the University of California, Santa Cruz.
•
http://www.mcs.vuw.ac.nz/courses/COMP305/200
4T1/Tutorials/
• You need good C++ background.
• Good command of the concept of your OS
course. A Quick Introduction to C++ can be
found in this link (pdf)
• Nachos is coded in a subset of C++; a
• A strong WILL to Work in a team!!
• Good Luck
• First step:
Installing Nachos
Obtaining the source code (use the
link below to get the nachos code).
Download Nachos.
Installation steps:
To install Nachos you need to:
1. download Nachos sources from
the above link.
2. unpack. (suppose you saved the
source in file "nachos.tar.gz")
linux% gunzip -c
nachos.tar.gz
3. unarchive the file
linux% tar -xvf
nachos.tar
4. configure for OS For a linux
machine:
linux% cd Nachos
linux% cp
5. compile.
linux% make
Running Nachos
• What parts of Nachos should we
modify?
• Don’t Panic
• you may find the nature of the Nachos
projects confusing, because there is a
significant amount of code already
written and it is not always clear what
to change
• In most cases, you will be adding new code
to the existing framework, mostly in new
procedures or classes that you define and
create.
• In a few cases you will be extending or
"filling in" C++ classes or methods that are
already defined.
• Very rarely will it be necessary to delete or
rewrite code that already exists, or to add
code in areas outside of the main focus of
each assignment
• the simplest and most direct solutions for
each assignment do not require you to
modify code outside of the primary area of
focus for each assignment.
• under no circumstances should you modify
the behavior of the "hardware" as defined
by the machine simulation software in the
machine/ subdirectory
• It is acceptable to change #define directives
that determine the machine or system
parameters (e.g., size of physical memory or
size of the default stack)
• Tracing and debugging Nachos
programs There are at least three ways
to trace execution: (1) add printf (or
fprintf) statements to the code, (2) use
the gdb debugger or another debugger
of your choosing, and (3) insert calls to
the DEBUG function that Nachos
provides.
• different components of Nachos
• As in an actual operating system, there is a
Scheduler that keeps track of all the
processes in the system and switches
between them periodically. It maintains a
list of processes, readyList, for this purpose.
• All processes within Nachos execute as
threads. This means that all programs that
run on Nachos are implemented as threads
• The time slice for which each process
executes is determined by the Timer
which causes an interrupt when the
time slice for the currently executing
process is over. The Interrupt
component handles all interrupts
generated by the Timer. The cause of
the interrupts may be device I/O, e.g. a
write to a file by a process, a request
by some process to take input from the
console (screen) or it may be a signal
for the scheduler to switch to another
process.
• In Nachos, the global variable currentThread
always points to the thread currently occupying
the CPU. Do not worry about
threadToBeDestroyed for now, that is explained in
the Threads section.
• All operating systems need some hardware to
execute processes. The Nachos Machine provides
this functionality - it simulates the MIPS
architecture
• All operating systems need some hardware to
execute processes. The Nachos Machine provides
this functionality - it simulates the MIPS
architecture. The FileSystem component manages
all file operations. SynchDisk provides
synchronized access to the disk. To connect our
machine to other machines, we have a PostOffice
that manages receipt and sending of messages
across the network. Stats is a component within
Nachos that maintains certain statistics about the
execution and performance of the system. For
example, it may keep track of how many
characters have been written to the screen, how
many page faults have occurred, how many
packets have been received across the network etc.
• -
• the names are very descriptive of what these
actually contain.
• Remember that in any program, execution always
begins from the main() routine. For Nachos, this
routine is contained in main.cc in the threads
directory. For each lab, execution always begins in
main.cc but flow of control depends on which lab
you are attempting. This functionality is provided
through the #ifdef directives in main.cc. The
arguments to this directive are defined in the
makefiles.
• What are they?
A traditional process has a single thread of
control and a single program counter. Modern
operating systems provide multiple threads of
control within a process - these are called
Threads or lightweight processes. All threads
within a process share the same address
space. This means that a thread shares its
code and data section with other threads.
Each, however, has its own program counter,
stack and register set. The program counter
determines which instruction the thread is
currently executing.
• This may or may not be the same as that for
other threads. The register set is needed
because when threads are suspended, their
state must be saved. Upon resumption, this
state is loaded into the machine registers.
• Like traditional processes, threads can be in
different states - running, ready or blocked .
• Why have them?
To see why multiple threads of control within a
process are useful, consider what a web browser
such as Netscape must do to load a web page
containing multiple images. For each image, it
must set up a separate connection to the page's
home site and request the image. By having
multiple threads within the browser process, the
images can be requested in parallel with the user
scrolling through the content that has already been
received, greatly enhancing the response time for
the user.
• Also, extensive sharing of resources makes
CPU switching between threads
inexpensive, compared with switching
between traditional or heavyweight
processes. A register-set switch is still
required but no memory-management
related work needs to be done
• How?
Two kinds of implementation schemes are in
vogue for threads - user-level and kernel-level.
User-level threads are managed entirely in user
space. The operating system is not aware of their
existence. Thread switching does not involve a
call to the operating system or an interrupt to the
kernel. Switching is thus independent of the
operating system and very quick. One
disadvantage of this scheme is that if a user-level
thread blocks while executing a system call, the
kernel blocks the entire process since it is not even
aware that other threads exist
• In systems supporting kernel-level threads, the
operating system is aware of the existence of
multiple threads per process, so when a thread
blocks, the operating system chooses the next one
to run, either from the same process or a different
one.
• Some systems support a hybrid approach in which
both user-level and kernel-level threads are
supported. Solaris 2 is one such system.
• Threads in Nachos
• In the discussion that follows, we explain
all operations on threads in Nachos,
focusing on implementation details to
enable you to understand what happens
inside Nachos when you invoke those
operations
• four basic operations on threads: Fork,
Yield, Sleep, and Finish. One
implementation detail to remember when
using threads is that forking a thread takes
two steps. The first involves creating a
thread by allocating a data structure for it.
Then you can invoke Fork() on the thread
object just created.
• The above code creates 4 threads using the
constructor function for the class Thread. The
constructor function simply allocates space for
thread and sets its status to JUST_CREATED.
Note that in Nachos, a "main" thread is created as
part of system initialization (see system.cc in
code/threads ). Thus, the above code is executed
by "main". Note that the ready list remains empty
upon creation of these threads.
• In Nachos, the global variable currentThread
always points to the thread currently occupying
the CPU.
• The above code completes the job of forking off the
threads. Fork() allocates a stack for the thread
which invokes it, and adds it to the ready list
maintained by the scheduler (see scheduler.cc in
code/threads ). "somefunction" is the name of the
function that the thread will execute upon
occupying the CPU. 0 is the argument to that
function. The routine StackAllocate() allocates and
initializes the execution stack for the thread. A C
routine ThreadRoot() is called which calls the
function "somefunction" and upon its return, calls
ThreadFinish(). Note that now, we have all four
threads in the ready list with their status set to
READY. currentThread still points to "main".
• Thread Switching
• The figure shows the CPU switching between
threads t0 and t1. After the switch, t0 is back in
the ready list and t1 occupies the CPU. The
switch takes place when the routine Run() is
invoked. This routine is part of the Scheduler
class (see scheduler.cc in the threads directory).
Upon invocation, the routine saves the state of
the thread currently occupying the CPU and
loads the state of the thread being switched to,
into the machine registers.
•
• The actual switch takes place by calling the
machine dependent context switch routine,
SWITCH, defined in switch.s in the threads
directory. An important point to note in the
Run() routine is what happens after the
SWITCH routine returns. All the statements
in the Run() routine after the call to SWITCH
are executed by the new thread we just
switched to! Thus, the thread that invoked the
Run() routine is not the same as the one that
finishes its execution
• Thread Finish and Sleep
• Finish() is called when a thread is done
executing its forked procedure. As a result,
the thread invoking Finish() ceases to exist
and the thread at the head of the ready list is
assigned the CPU. Thus, a switch takes place
here as well. In Nachos, the global variable
threadToBeDestroyed always points to the
thread that is to be deallocated. Its default
value is NULL and is set by a thread that
invokes Finish().
• Assume the CPU is occupied by thread t2
and the following code is executed:
• currentThread->Finish(); As a first step, the
thread that invokes Finish(), sets the variable
threadToBeDestroyed to itself and then
invokes Sleep .
• The above figure shows the state of the
system right before Sleep() is invoked by
currentThread. Remember that
currentThread is also the thread that invoked
Finish() .
• The Sleep() routine sets the status of the
invoking thread to BLOCKED and invokes
Run() to switch to the thread at the front of
the ready list. The thread is actually
deallocated inside the Run() routine since it
can only be deallocated after it has given up
the CPU.
• Sleep() can be invoked by either a thread that
wants to finish or when it is blocked waiting
on a synchronization variable. In the first
case, the finishing thread sets
threadToBeDestroyed causing it to be
deallocated by the thread being switched to
inside the Run() routine. In the second case,
the blocked thread will eventually be woken
up by some other thread and put back on the
ready list.