/c/cs422/nachos/code/machine
. Other
components (such as thread management) reside under the directory
/c/cs422/nachos/code
.
There will be a Nachos programming assignment for each of the following topics:
Nachos runs as a UNIX user process; your operating system code and the machine simulator are linked together into a single executable. In reality, operating systems run on bare hardware (without machine simulator code). This hardware handles everything from interrupts to registers to page tables. User processes run on top of the operating system and make system calls, calls to operating system functions in the kernel. The concept of using a simulated machine is a useful one in operating system design, as it can be significantly more difficult to debug and test an operating system running on real, bare hardware. While the Nachos machine simulator functions well as a platform for developing operating system code and corresponds roughly to the physical hardware of a DECstation, its emulation is by no means exhaustive.
machine
. You should
use this list to guide you in reviewing all of the existing code in the system. The
description also includes questions to help you notice and pinpoint some of the most
important parts of the system. The key part of this exercise is understanding the base
system. Your goal is to clearly grasp how it all fits together so that you can make
intelligent design decisions when you approach future assignments. This may seem
tedious, but if you understand how the base system works now, you will have much less
difficulty completing future assignments.
These questions are not meant to be tricky--most of the answers can be found in comments in the Nachos source and in the Nachos Roadmap, though you may have to look elsewhere (such as the textbook) for some backgound information.
Machine
and Instruction
classes.
Machine::DelayedLoad
- executes a delayed load.
Instruction::Decode
- sets source, destination, and target registers, and extracts
the opcode.
TypeToReg
- fetches a register number from an instruction.
Mult
- implements R2000 multiplication.
2. How many registers can you possibly have in a single instruction?
3. What is the minimum number of MIPS assembly instructions needed to add the contents of two registers, and store the result back to memory? Why can't this be done in a single instruction?
Machine
class.
Machine::Machine
- creates the machine and initializes all the registers, calling on
a lot of other constructors to "put the machine together." For example, TLB or page
tables are created here, as is the memory array.
Machine::~Machine
- deallocates machine resources.
Machine::RaiseException
- simulates a machine exception; saves required state
and dispatches to the appropriate exception handler.
Machine::Debugger
- implements a very crude debugging environment for user-
level programs.
Machine::DumpState
- used for debugging; dumps out the machine registers.
Machine::ReadRegister
- returns the value stored in a simulated register.
Machine::WriteRegister
- sets the value of a register.
CheckEndian
- verifies that the underlying hardware uses correct byte-ordering.
2. Let's say that you wanted to execute: register 3 gets register 1 + register 2. What single C statement, involving three function calls, could you use to do this?
3. If you execute DumpState()
, will you see the contents of the memory of your
Nachos machine?
4. How many registers are there in this machine?
5. Can this machine have both a TLB and a page table?
Console
class. The console is the controlling terminal of a machine. We
conventionally think of the console (or a terminal) as a single device, but in reality there
are two: the keyboard for input and the display for output.
Console::Console
- creates the devices and initializes the handlers for input and
output.
Console::~Console
- frees up console resources.
Console::CheckCharAvail
- checks if there is a character to read; if so, reads it
and calls the appropriate handler. Also schedules the next interrupt to check for a
character.
Console::WriteDone
- calls the write handler when a write has completed.
Console::GetChar
- processes a character read from the console.
Console::PutChar
- sets up a character for output.
Console::Console()
.
We want to know where the constructor is called from.)
2. What are its initial read and write handlers?
3. Where is the function Read()
(used by
Console::CheckCharAvail())
defined? Why does the code
invoke Read()
here, and not ReadPartial()
?
4. What are the status flags in the Console
class and what do they mean?
Disk
class and sets disk constants.
Disk::Disk
- creates the disk object, and opens and initializes the file that simulates
the disk.
Disk::~Disk
- frees disk resources.
Disk::PrintSector
- used for debugging; prints the contents of a disk sector.
Disk::ReadRequest
- performs a read from the disk file to simulate a disk I/O and
schedules an interrupt to simulate I/O completion.
Disk::WriteRequest
- writes bytes to the disk file to simulate an I/O and schedules
an interrupt to indicate completion.
Disk::HandleInterrupt
- disk interrupt handler; used to dispatch to the kernel.
Disk::TimeToSeek
- used in computing the length of time to perform an I/O.
Disk::ModuloDiff
- determines the rotational delay.
Disk::ComputeLatency
- computes the total time for an I/O request.
Disk::UpdateLast
- records the position of the last I/O.
Interrupt
and Pending-
Interrupt
classes.
PendingInterrupt::PendingInterrupt
- schedules an interrupt.
Interrupt::Interrupt
- creates an empty list of pending interrupts and initializes
interrupt state variables.
Interrupt::~Interrupt
- destroys all interrupt resources.
Interrupt::ChangeLevel
- changes the interrupt level.
Interrupt::SetLevel
- initiates a new interrupt level.
Interrupt::Enable
- turns interrupts on.
Interrupt::OneTick
- simulates one tick of machine time.
Interrupt::YieldOnReturn
- sets a flag so that the current process is
descheduled upon the return of the current interrupt.
Interrupt::Idle
- advances time to the next event.
Interrupt::Halt
- shuts down the machine.
Interrupt::Schedule
- creates a PendingInterrupt
and puts it on the queue.
Interrupt::CheckIfDue
- checks the queue to see if an interrupt is scheduled to
happen.
PrintPending
- used for debugging; prints all pending interrupts.
DumpState
- dumps interrupt state.
2. The machine currently has only two interrupt states (on/off). Describe how you could modify the machine to have many different interrupt levels. As you do later assignments, think about places where having different interrupt levels might be useful.
3. Explain the purpose of having both ChangeLevel()
and SetLevel()
.
4. Why is OneTick()
a function of Interrupt
instead of Machine
?
5. What kind of time accounting is performed?
6. When can interrupt levels actually change?
7. When do context switches happen?
8. How is the clock advanced in CheckIfDue()
?
9. Why is DelayedLoad()
called in CheckIfDue()
?
PacketHeader
and Network
classes which simulate a physical network.
Network::Network
- creates the network connection and initializes variables like
reliability
(from 0-1.0) and addr
(integer address of this machine), as well as
read/write handlers which resemble those of the console device. Also opens a Unix
socket.
Network::~Network
- closes the Unix socket.
Network::CheckPktAvail
- scheduled at fixed intervals to check if a packet has
arrived over the network. Divides the packet into a header and data and then signals
the PostOffice
.
Network::SendDone
- indicates that the user can send another packet.
Network::Send
- sends a packet over the network (concatenating the header and
data). Then, schedules an interrupt by SendDone()
.
Network::Receive
- reads a packet (if there is one) from the inbox.
Statistics::Statistics
- initializes all counters to zero.
Statistics::Print
- prints the values of all performance metrics.
Timer
class.
TimerTicks
.
Timer::Timer
- stores the function and args to call on each interrupt. Also, stores
an indicator of whether interrupts should be at random intervals.
Timer::TimerExpired
- schedules the next interrupt, and calls the interrupt
handler (since the timer has expired).
Timer::TimeOfNextInterrupt
- returns either TimerTicks
or a random time
interval.
2. What command-line flag causes the boolean, doRandom
, to be set to TRUE
?
3. Why isn't TimerHandler()
a member function?
TranslationEntry
class (used for page table and TLB entries).
Machine::ReadMem/WriteMem
- reads/writes 1, 2, or 4 bytes of memory at a
specific address into/from a buffer. The translation from virtual to physical memory is
handled by Translate
. Also, checks if an exception was raised by the address
translation.
Machine::Translate
- converts a virtual address to a physical one. Also performs
various error and exception checking (alignment, page fault, bus error, etc.). Uses
either the page table or the TLB for translation.
/c/cs422/as0
, you will find the source
code for a List
class, and a small test program. This
implementation of lists was
copied from the List
class used in the Nachos
threads
system. The source code for the two versions of
List
are essentially identical; the version you will use
here was only modified to allow it to be compiled without the rest of
Nachos.
To try out the program, do the following:
$ cd ~/cs422 $ mkdir as0 $ cd as0 $ cp /c/cs422/as0/* .These commands create a working directory,
~/cs422/as0
in
which you can work on this assignment, and copy the source files from
the class directory.
This will build a program, test1
, from two C++ source
files:
List
class
List
class.
You should be able to run the program test1
, which should
give the following output:
$ ./test1 this is a test $
If you read the source code to test1.cc
, you will notice
that it first adds some data to the list (a bunch of strings), and
then prints the contents of the list by removing each element from the list.
If you study the code that prints the contents of the list, you might
notice that the design of this List
class is somewhat
limited, in that the
only way to examine the contents of a list is to remove all the
elements from the list! That is, there is no way to simply iterate
through the contents of the list without destroying the list in the process.
To remedy this deficiency, we can introduce another class,
ListIterator
. A ListIterator
allows code outside of the
List
class to iterate over the contents of a list without destroying the list. The ListIterator
class is defined in the file
listiterator.h
, and is used in test2.cc
.
You should start by reading the code in test2.cc
to understand how
the ListIterator
class is used, and how a
ListIterator
it is obtained
from a List
.
In the supplied Makefile
, there is already a target for
test2
. If you type make test2
,
make
will attempt to build test2
. But this
will elicit compilation errors, because iterators have not been
implemented yet.
You should do the following:
list.h
and list.cc
to allow the
user to obtain an instance of ListIterator
for a list.
listiterator.h
file to add whatever private
member variable(s) that are needed.
listiterator.cc
, which
contains your implementation of the ListIterator
class.
Makefile
so that typing 'make' will
automatically build
both test1 and test2. Note, too, that the changes you introduce to
get test2 working may inadvertently cause test1 to break, because the
rules for test1 do not include the 'listiterator.o' object file.
Note: When you modify existing source files, you must mark changes using the commenting convention described in Assignment Mechanics!
The whole point of the previous excercise was to implement a non-destructive iterator class, which would not destroy a list in the process of iterating over the elements.
Joe Dimwit isn't a very clever programmer, and doesn't read too
closely. He implemented a version of the ListIterator
class that simply returns the result of a call to
List::Remove()
every time that
ListIterator::GetNext()
is called.
Unfortunately, Joe Dimwit's version of test2
works, and
produces the same output as your version! Why?
You should modify the test case test2.cc
in such a way
that it will fail with Joe Dimwit's implementation of
ListIterator
, but will work with your version. If you wish,
you may change the output that test2
produces. The
description of this test, along with sample output, should be placed
in your TESTCASES
file.
You should submit your answers to section 3 along with a printout of your README for section 4 to the TA's mailbox.
You should submit your
sources to this assignment using the standard
Don't forget that no late submissions will be accepted for this
assignment!
submit
script. The easiest way to do this is to submit
$ make clean
$ /c/cs422/bin/submit 0 *