master
ginger88895 2019-03-31 04:08:58 +08:00
commit 33ebdf5cfd
251 changed files with 13131 additions and 0 deletions

54
Makefile Normal file
View File

@ -0,0 +1,54 @@
JAVADOCPARAMS = -doctitle "Nachos 5.0 Java" -protected \
-link http://java.sun.com/j2se/1.5.0/docs/api/
machine = Lib Config Stats Machine TCB \
Interrupt Timer \
Processor TranslationEntry \
SerialConsole StandardConsole \
OpenFile OpenFileWithPosition ArrayFile FileSystem StubFileSystem \
ElevatorBank ElevatorTest ElevatorGui \
ElevatorControls ElevatorEvent ElevatorControllerInterface \
RiderControls RiderEvent RiderInterface \
Kernel Coff CoffSection \
NetworkLink Packet MalformedPacketException
security = Privilege NachosSecurityManager
ag = AutoGrader BoatGrader
threads = ThreadedKernel KThread Alarm \
Scheduler ThreadQueue RoundRobinScheduler \
Semaphore Lock Condition SynchList \
Condition2 Communicator Rider ElevatorController \
PriorityScheduler LotteryScheduler Boat
userprog = UserKernel UThread UserProcess SynchConsole
vm = VMKernel VMProcess
network = NetKernel NetProcess PostOffice MailMessage
ALLDIRS = machine security ag threads userprog vm network
PACKAGES := $(patsubst %,nachos.%,$(ALLDIRS))
CLASSFILES := $(foreach dir,$(DIRS),$(patsubst %,nachos/$(dir)/%.class,$($(dir))))
.PHONY: all rmtemp clean doc hwdoc swdoc
all: $(CLASSFILES)
nachos/%.class: ../%.java
javac -classpath . -d . -sourcepath ../.. -g $<
clean:
rm -f */*/*.class
doc:
mkdir -p ../doc
javadoc $(JAVADOCPARAMS) -d ../doc -sourcepath .. $(PACKAGES)
test:
cd ../test ; gmake
ag: $(patsubst ../ag/%.java,nachos/ag/%.class,$(wildcard ../ag/*.java))

270
README Normal file
View File

@ -0,0 +1,270 @@
Nachos for Java README
Welcome to Nachos for Java. We believe that working in Java rather than
C++ will greatly simplify the development process by preventing bugs
arising from memory management errors, and improving debugging support.
Getting Nachos:
Download nachos-java.tar.gz from the Projects section of the class
homepage at:
http://www-inst.EECS.Berkeley.EDU/~cs162/
Unpack it with these commands:
gunzip -c nachos-java.tar.gz | tar xf -
Additional software:
Nachos requires the Java Devlopment Kit, version 1.5 or later. This is
installed on all instructional machines in:
/usr/sww/lang/jdk-1.5.0_05
To use this version of the JDK, be sure that
/usr/sww/lang/jdk-1.5.0_05/bin
is on your PATH. (This should be the case for all class accounts
already.)
If you are working at home, you will need to download the JDK.
It is available from:
http://java.sun.com/j2se/1.5/
Please DO NOT DOWNLOAD the JDK into your class account! Use the
preinstalled version instead.
The build process for Nachos relies on GNU make. If you are running on
one of the instructional machines, be sure you run 'gmake', as 'make'
does not support all the features used. If you are running Linux, the
two are equivalent. If you are running Windows, you will need to
download and install a port. The most popular is the Cygnus toolkit,
available at:
http://sources.redhat.com/cygwin/mirrors.html
The Cygnus package includes ports of most common GNU utilities to
Windows.
For project 2, you will need a MIPS cross compiler, which is a
specially compiled GCC which will run on one architecture (e.g.
Sparc) and produce files for the MIPS processor. These compilers
are already installed on the instructional machines, and are
available in the directory specified by the $ARCHDIR environment
variable.
If you are working at home, you will need to get a cross-compiler
for yourself. Cross-compilers for Linux and Win32 will be available
from the CS162 Projects web page. Download the cross compiler
distribution and unpack it with the following command:
gunzip -c mips-x86-linux-xgcc.tar.gz | tar xf -
(Substitute the appropriate file name for mips-x86.linux-xgcc in the
above command.) You need to add the mips-x86.linux-xgcc directory to
your PATH, and set an environment variable ARCHDIR to point to this
directory. (Again, this has already been done for you on the
instructional machines.)
Compiling Nachos:
You should now have a directory called nachos, containing a Makefile,
this README, and a number of subdirectories.
First, put the 'nachos/bin' directory on your PATH. This directory
contains the script 'nachos', which simply runs the Nachos code.
To compile Nachos, go to the subdirectory for the project you wish
to compile (I will assume 'proj1/' for Project 1 in my examples),
and run:
gmake
This will compile those portions of Nachos which are relevant to the
project, and place the compiled .class files in the proj1/nachos
directory.
You can now test Nachos from the proj1/ directory with:
nachos
You should see output resembling the following:
nachos 5.0j initializing... config interrupt timer elevators user-check grader
*** thread 0 looped 0 times
*** thread 1 looped 0 times
*** thread 0 looped 1 times
*** thread 1 looped 1 times
*** thread 0 looped 2 times
*** thread 1 looped 2 times
*** thread 0 looped 3 times
*** thread 1 looped 3 times
*** thread 0 looped 4 times
*** thread 1 looped 4 times
Machine halting!
Ticks: total 24750, kernel 24750, user 0
Disk I/O: reads 0, writes 0
Console I/O: reads 0, writes 0
Paging: page faults 0, TLB misses 0
Network I/O: received 0, sent 0
This is the correct output for the "bare bones" Nachos, without any of
the features you will add during the projects.
If you are working on a project which runs user programs (projects 2-4),
you will also need to compile the MIPS test programs with:
gmake test
Command Line Arguments:
For a summary of the command line arguments, run:
nachos -h
The commands are:
-d <debug flags>
Enable some debug flags, e.g. -d ti
-h
Print this help message.
-s <seed>
Specify the seed for the random number generator
-x <program>
Specify a program that UserKernel.run() should execute,
instead of the value of the configuration variable
Kernel.shellProgram
-z
print the copyright message
-- <grader class>
Specify an autograder class to use, instead of
nachos.ag.AutoGrader
-# <grader arguments>
Specify the argument string to pass to the autograder.
-[] <config file>
Specifiy a config file to use, instead of nachos.conf
Nachos offers the following debug flags:
c: COFF loader info
i: HW interrupt controller info
p: processor info
m: disassembly
M: more disassembly
t: thread info
a: process info (formerly "address space", hence a)
To use multiple debug flags, clump them all together. For example, to
monitor coff info and process info, run:
nachos -d ac
nachos.conf:
When Nachos starts, it reads in nachos.conf from the current
directory. It contains a bunch of keys and values, in the simple
format "key = value" with one key/value pair per line. To change the
default scheduler, default shell program, to change the amount of
memory the simulator provides, or to reduce network reliability, modify
this file.
Machine.stubFileSystem:
Specifies whether the machine should provide a stub file system. A
stub file system just provides direct access to the test directory.
Since we're not doing the file system project, this should always
be true.
Machine.processor:
Specifies whether the machine should provide a MIPS processor. In
the first project, we only run kernel code, so this is false. In
the other projects it should be true.
Machine.console:
Specifies whether the machine should provide a console. Again, the
first project doesn't need it, but the rest of them do.
Machine.disk:
Specifies whether the machine should provide a simulated disk. No
file system project, so this should always be false.
ElevatorBank.allowElevatorGUI:
Normally true. When we grade, this will be false, to prevent
malicious students from running a GUI during grading.
NachosSecurityManager.fullySecure:
Normally false. When we grade, this will be true, to enable
additional security checks.
Kernel.kernel:
Specifies what kernel class to dynmically load. For proj1, this is
nachos.threads.ThreadedKernel. For proj2, this should be
nachos.userprog.UserKernel. For proj3, nachos.vm.VMKernel. For
proj4, nachos.network.NetKernel.
Processor.usingTLB:
Specifies whether the MIPS processor provides a page table
interface or a TLB interface. In page table mode (proj2), the
processor accesses an arbitrarily large kernel data structure to do
address translation. In TLB mode (proj3 and proj4), the processor
maintains a small TLB (4 entries).
Processor.numPhysPages:
The number of pages of physical memory. Each page is 1K. This is
normally 64, but we can lower it in proj3 to see whether projects
thrash or crash.
Documentation:
The JDK provides a command to create a set of HTML pages showing all
classes and methods in program. We will make these pages available on
the webpage, but you can create your own for your home machine by doing
the following (from the nachos/ directory):
mkdir ../doc
gmake doc
Troubleshooting:
If you receive an error about "class not found exception", it may be
because you have not set the CLASSPATH environment variable. Add the
following to your .cshrc:
setenv CLASSPATH .
Credits:
Nachos was originally written by Wayne A. Christopher, Steven J.
Procter, and Thomas E. Anderson. It incorporates the SPIM simulator
written by John Ousterhout. Nachos was rewritten in Java by Daniel
Hettena.
Copyright:
Copyright (c) 1992-2001 The Regents of the University of California.
All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without written
agreement is hereby granted, provided that the above copyright notice
and the following two paragraphs appear in all copies of this
software.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
ENHANCEMENTS, OR MODIFICATIONS.

279
ag/AutoGrader.java Normal file
View File

@ -0,0 +1,279 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.ag;
import nachos.machine.*;
import nachos.security.*;
import nachos.threads.*;
import java.util.Hashtable;
import java.util.StringTokenizer;
/**
* The default autograder. Loads the kernel, and then tests it using
* <tt>Kernel.selfTest()</tt>.
*/
public class AutoGrader {
/**
* Allocate a new autograder.
*/
public AutoGrader() {
}
/**
* Start this autograder. Extract the <tt>-#</tt> arguments, call
* <tt>init()</tt>, load and initialize the kernel, and call
* <tt>run()</tt>.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
*/
public void start(Privilege privilege) {
Lib.assertTrue(this.privilege == null,
"start() called multiple times");
this.privilege = privilege;
String[] args = Machine.getCommandLineArguments();
extractArguments(args);
System.out.print(" grader");
init();
System.out.print("\n");
kernel =
(Kernel) Lib.constructObject(Config.getString("Kernel.kernel"));
kernel.initialize(args);
run();
}
private void extractArguments(String[] args) {
String testArgsString = Config.getString("AutoGrader.testArgs");
if (testArgsString == null) {
testArgsString = "";
}
for (int i=0; i<args.length; ) {
String arg = args[i++];
if (arg.length() > 0 && arg.charAt(0) == '-') {
if (arg.equals("-#")) {
Lib.assertTrue(i < args.length,
"-# switch missing argument");
testArgsString = args[i++];
}
}
}
StringTokenizer st = new StringTokenizer(testArgsString, ",\n\t\f\r");
while (st.hasMoreTokens()) {
StringTokenizer pair = new StringTokenizer(st.nextToken(), "=");
Lib.assertTrue(pair.hasMoreTokens(),
"test argument missing key");
String key = pair.nextToken();
Lib.assertTrue(pair.hasMoreTokens(),
"test argument missing value");
String value = pair.nextToken();
testArgs.put(key, value);
}
}
String getStringArgument(String key) {
String value = (String) testArgs.get(key);
Lib.assertTrue(value != null,
"getStringArgument(" + key + ") failed to find key");
return value;
}
int getIntegerArgument(String key) {
try {
return Integer.parseInt(getStringArgument(key));
}
catch (NumberFormatException e) {
Lib.assertNotReached("getIntegerArgument(" + key + ") failed: " +
"value is not an integer");
return 0;
}
}
boolean getBooleanArgument(String key) {
String value = getStringArgument(key);
if (value.equals("1") || value.toLowerCase().equals("true")) {
return true;
}
else if (value.equals("0") || value.toLowerCase().equals("false")) {
return false;
}
else {
Lib.assertNotReached("getBooleanArgument(" + key + ") failed: " +
"value is not a boolean");
return false;
}
}
long getTime() {
return privilege.stats.totalTicks;
}
void targetLevel(int targetLevel) {
this.targetLevel = targetLevel;
}
void level(int level) {
this.level++;
Lib.assertTrue(level == this.level,
"level() advanced more than one step: test jumped ahead");
if (level == targetLevel)
done();
}
private int level = 0, targetLevel = 0;
void done() {
System.out.print("\nsuccess\n");
privilege.exit(162);
}
private Hashtable<String, String> testArgs =
new Hashtable<String, String>();
void init() {
}
void run() {
kernel.selfTest();
kernel.run();
kernel.terminate();
}
Privilege privilege = null;
Kernel kernel;
/**
* Notify the autograder that the specified thread is the idle thread.
* <tt>KThread.createIdleThread()</tt> <i>must</i> call this method before
* forking the idle thread.
*
* @param idleThread the idle thread.
*/
public void setIdleThread(KThread idleThread) {
}
/**
* Notify the autograder that the specified thread has moved to the ready
* state. <tt>KThread.ready()</tt> <i>must</i> call this method before
* returning.
*
* @param thread the thread that has been added to the ready set.
*/
public void readyThread(KThread thread) {
}
/**
* Notify the autograder that the specified thread is now running.
* <tt>KThread.restoreState()</tt> <i>must</i> call this method before
* returning.
*
* @param thread the thread that is now running.
*/
public void runningThread(KThread thread) {
privilege.tcb.associateThread(thread);
currentThread = thread;
}
/**
* Notify the autograder that the current thread has finished.
* <tt>KThread.finish()</tt> <i>must</i> call this method before putting
* the thread to sleep and scheduling its TCB to be destroyed.
*/
public void finishingCurrentThread() {
privilege.tcb.authorizeDestroy(currentThread);
}
/**
* Notify the autograder that a timer interrupt occurred and was handled by
* software if a timer interrupt handler was installed. Called by the
* hardware timer.
*
* @param privilege proves the authenticity of this call.
* @param time the actual time at which the timer interrupt was
* issued.
*/
public void timerInterrupt(Privilege privilege, long time) {
Lib.assertTrue(privilege == this.privilege,
"security violation");
}
/**
* Notify the autograder that a user program executed a syscall
* instruction.
*
* @param privilege proves the authenticity of this call.
* @return <tt>true</tt> if the kernel exception handler should be called.
*/
public boolean exceptionHandler(Privilege privilege) {
Lib.assertTrue(privilege == this.privilege,
"security violation");
return true;
}
/**
* Notify the autograder that <tt>Processor.run()</tt> was invoked. This
* can be used to simulate user programs.
*
* @param privilege proves the authenticity of this call.
*/
public void runProcessor(Privilege privilege) {
Lib.assertTrue(privilege == this.privilege,
"security violation");
}
/**
* Notify the autograder that a COFF loader is being constructed for the
* specified file. The autograder can use this to provide its own COFF
* loader, or return <tt>null</tt> to use the default loader.
*
* @param file the executable file being loaded.
* @return a loader to use in loading the file, or <tt>null</tt> to use
* the default.
*/
public Coff createLoader(OpenFile file) {
return null;
}
/**
* Request permission to send a packet. The autograder can use this to drop
* packets very selectively.
*
* @param privilege proves the authenticity of this call.
* @return <tt>true</tt> if the packet should be sent.
*/
public boolean canSendPacket(Privilege privilege) {
Lib.assertTrue(privilege == this.privilege,
"security violation");
return true;
}
/**
* Request permission to receive a packet. The autograder can use this to
* drop packets very selectively.
*
* @param privilege proves the authenticity of this call.
* @return <tt>true</tt> if the packet should be delivered to the kernel.
*/
public boolean canReceivePacket(Privilege privilege) {
Lib.assertTrue(privilege == this.privilege,
"security violation");
return true;
}
private KThread currentThread;
}

81
ag/BoatGrader.java Normal file
View File

@ -0,0 +1,81 @@
package nachos.ag;
public class BoatGrader {
/**
* BoatGrader consists of functions to be called to show that
* your solution is properly synchronized. This version simply
* prints messages to standard out, so that you can watch it.
* You cannot submit this file, as we will be using our own
* version of it during grading.
* Note that this file includes all possible variants of how
* someone can get from one island to another. Inclusion in
* this class does not imply that any of the indicated actions
* are a good idea or even allowed.
*/
//NEW ADDITION FOR 2014
//MUST BE CALLED AT THE START OF CHILDITINERARY!
public void initializeChild(){
System.out.println("A child has forked.");
}
//NEW ADDITION FOR 2014
//MUST BE CALLED AT THE START OF ADULTITINERARY!
public void initializeAdult(){
System.out.println("An adult as forked.");
}
/* ChildRowToMolokai should be called when a child pilots the boat
from Oahu to Molokai */
public void ChildRowToMolokai() {
System.out.println("**Child rowing to Molokai.");
}
/* ChildRowToOahu should be called when a child pilots the boat
from Molokai to Oahu*/
public void ChildRowToOahu() {
System.out.println("**Child rowing to Oahu.");
}
/* ChildRideToMolokai should be called when a child not piloting
the boat disembarks on Molokai */
public void ChildRideToMolokai() {
System.out.println("**Child arrived on Molokai as a passenger.");
}
/* ChildRideToOahu should be called when a child not piloting
the boat disembarks on Oahu */
public void ChildRideToOahu() {
System.out.println("**Child arrived on Oahu as a passenger.");
}
/* AdultRowToMolokai should be called when a adult pilots the boat
from Oahu to Molokai */
public void AdultRowToMolokai() {
System.out.println("**Adult rowing to Molokai.");
}
/* AdultRowToOahu should be called when a adult pilots the boat
from Molokai to Oahu */
public void AdultRowToOahu() {
System.out.println("**Adult rowing to Oahu.");
}
/* AdultRideToMolokai should be called when an adult not piloting
the boat disembarks on Molokai */
public void AdultRideToMolokai() {
System.out.println("**Adult arrived on Molokai as a passenger.");
}
/* AdultRideToOahu should be called when an adult not piloting
the boat disembarks on Oahu */
public void AdultRideToOahu() {
System.out.println("**Adult arrived on Oahu as a passenger.");
}
}

3
ag/package.html Normal file
View File

@ -0,0 +1,3 @@
<body>
Provides classes that can be used to automatically grade Nachos projects.
</body>

15
bin/nachos Normal file
View File

@ -0,0 +1,15 @@
#!/bin/sh
# Shell-script front-end to run Nachos.
# Simply sets terminal to a minimum of one byte to complete a read and
# disables character echo. Restores original terminal state on exit.
onexit () {
stty $OLDSTTYSTATE
}
OLDSTTYSTATE=`stty -g`
trap onexit 0
stty -icanon min 1 -echo
java nachos.machine.Machine $*

44
machine/ArrayFile.java Normal file
View File

@ -0,0 +1,44 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* A read-only <tt>OpenFile</tt> backed by a byte array.
*/
public class ArrayFile extends OpenFileWithPosition {
/**
* Allocate a new <tt>ArrayFile</tt>.
*
* @param array the array backing this file.
*/
public ArrayFile(byte[] array) {
this.array = array;
}
public int length() {
return array.length;
}
public void close() {
array = null;
}
public int read(int position, byte[] buf, int offset, int length) {
Lib.assertTrue(offset >= 0 && length >= 0 && offset+length <= buf.length);
if (position < 0 || position >= array.length)
return 0;
length = Math.min(length, array.length-position);
System.arraycopy(array, position, buf, offset, length);
return length;
}
public int write(int position, byte[] buf, int offset, int length) {
return 0;
}
private byte[] array;
}

151
machine/Coff.java Normal file
View File

@ -0,0 +1,151 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import java.io.EOFException;
/**
* A COFF (common object file format) loader.
*/
public class Coff {
/**
* Allocate a new Coff object.
*/
protected Coff() {
file = null;
entryPoint = 0;
sections = null;
}
/**
* Load the COFF executable in the specified file.
*
* <p>
* Notes:
* <ol>
* <li>If the constructor returns successfully, the file becomes the
* property of this loader, and should not be accessed any further.
* <li>The autograder expects this loader class to be used. Do not load
* sections through any other mechanism.
* <li>This loader will verify that the file is backed by a file system,
* by asserting that read() operations take non-zero simulated time to
* complete. Do not supply a file backed by a simulated cache (the primary
* purpose of this restriction is to prevent sections from being loaded
* instantaneously while handling page faults).
* </ol>
*
* @param file the file containing the executable.
* @exception EOFException if the executable is corrupt.
*/
public Coff(OpenFile file) throws EOFException {
this.file = file;
Coff coff = Machine.autoGrader().createLoader(file);
if (coff != null) {
this.entryPoint = coff.entryPoint;
this.sections = coff.sections;
}
else {
byte[] headers = new byte[headerLength+aoutHeaderLength];
if (file.length() < headers.length) {
Lib.debug(dbgCoff, "\tfile is not executable");
throw new EOFException();
}
Lib.strictReadFile(file, 0, headers, 0, headers.length);
int magic = Lib.bytesToUnsignedShort(headers, 0);
int numSections = Lib.bytesToUnsignedShort(headers, 2);
int optionalHeaderLength = Lib.bytesToUnsignedShort(headers, 16);
int flags = Lib.bytesToUnsignedShort(headers, 18);
entryPoint = Lib.bytesToInt(headers, headerLength+16);
if (magic != 0x0162) {
Lib.debug(dbgCoff, "\tincorrect magic number");
throw new EOFException();
}
if (numSections < 2 || numSections > 10) {
Lib.debug(dbgCoff, "\tbad section count");
throw new EOFException();
}
if ((flags & 0x0003) != 0x0003) {
Lib.debug(dbgCoff, "\tbad header flags");
throw new EOFException();
}
int offset = headerLength + optionalHeaderLength;
sections = new CoffSection[numSections];
for (int s=0; s<numSections; s++) {
int sectionEntryOffset = offset + s*CoffSection.headerLength;
try {
sections[s] =
new CoffSection(file, this, sectionEntryOffset);
}
catch (EOFException e) {
Lib.debug(dbgCoff, "\terror loading section " + s);
throw e;
}
}
}
}
/**
* Return the number of sections in the executable.
*
* @return the number of sections in the executable.
*/
public int getNumSections() {
return sections.length;
}
/**
* Return an object that can be used to access the specified section. Valid
* section numbers include <tt>0</tt> through <tt>getNumSections() -
* 1</tt>.
*
* @param sectionNumber the section to select.
* @return an object that can be used to access the specified section.
*/
public CoffSection getSection(int sectionNumber) {
Lib.assertTrue(sectionNumber >= 0 && sectionNumber < sections.length);
return sections[sectionNumber];
}
/**
* Return the program entry point. This is the value that to which the PC
* register should be initialized to before running the program.
*
* @return the program entry point.
*/
public int getEntryPoint() {
Lib.assertTrue(file != null);
return entryPoint;
}
/**
* Close the executable file and release any resources allocated by this
* loader.
*/
public void close() {
file.close();
sections = null;
}
private OpenFile file;
/** The virtual address of the first instruction of the program. */
protected int entryPoint;
/** The sections in this COFF executable. */
protected CoffSection sections[];
private static final int headerLength = 20;
private static final int aoutHeaderLength = 28;
private static final char dbgCoff = 'c';
}

228
machine/CoffSection.java Normal file
View File

@ -0,0 +1,228 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import java.io.EOFException;
import java.util.Arrays;
/**
* A <tt>CoffSection</tt> manages a single section within a COFF executable.
*/
public class CoffSection {
/**
* Allocate a new COFF section with the specified parameters.
*
* @param coff the COFF object to which this section belongs.
* @param name the COFF name of this section.
* @param executable <tt>true</tt> if this section contains code.
* @param readOnly <tt>true</tt> if this section is read-only.
* @param numPages the number of virtual pages in this section.
* @param firstVPN the first virtual page number used by this.
*/
protected CoffSection(Coff coff, String name, boolean executable,
boolean readOnly, int numPages, int firstVPN) {
this.coff = coff;
this.name = name;
this.executable = executable;
this.readOnly = readOnly;
this.numPages = numPages;
this.firstVPN = firstVPN;
file = null;
size = 0;
contentOffset = 0;
initialized = true;
}
/**
* Load a COFF section from an executable.
*
* @param file the file containing the executable.
* @param headerOffset the offset of the section header in the
* executable.
*
* @exception EOFException if an error occurs.
*/
public CoffSection(OpenFile file, Coff coff,
int headerOffset) throws EOFException {
this.file = file;
this.coff = coff;
Lib.assertTrue(headerOffset >= 0);
if (headerOffset+headerLength > file.length()) {
Lib.debug(dbgCoffSection, "\tsection header truncated");
throw new EOFException();
}
byte[] buf = new byte[headerLength];
Lib.strictReadFile(file, headerOffset, buf, 0, headerLength);
name = Lib.bytesToString(buf, 0, 8);
int vaddr = Lib.bytesToInt(buf, 12);
size = Lib.bytesToInt(buf, 16);
contentOffset = Lib.bytesToInt(buf, 20);
int numRelocations = Lib.bytesToUnsignedShort(buf, 32);
int flags = Lib.bytesToInt(buf, 36);
if (numRelocations != 0) {
Lib.debug(dbgCoffSection, "\tsection needs relocation");
throw new EOFException();
}
switch (flags & 0x0FFF) {
case 0x0020:
executable = true;
readOnly = true;
initialized = true;
break;
case 0x0040:
executable = false;
readOnly = false;
initialized = true;
break;
case 0x0080:
executable = false;
readOnly = false;
initialized = false;
break;
case 0x0100:
executable = false;
readOnly = true;
initialized = true;
break;
default:
Lib.debug(dbgCoffSection, "\tinvalid section flags: " + flags);
throw new EOFException();
}
if (vaddr%Processor.pageSize != 0 || size < 0 ||
initialized && (contentOffset < 0 ||
contentOffset+size > file.length())) {
Lib.debug(dbgCoffSection, "\tinvalid section addresses: " +
"vaddr=" + vaddr + " size=" + size +
" contentOffset=" + contentOffset);
throw new EOFException();
}
numPages = Lib.divRoundUp(size, Processor.pageSize);
firstVPN = vaddr / Processor.pageSize;
}
/**
* Return the COFF object used to load this executable instance.
*
* @return the COFF object corresponding to this section.
*/
public Coff getCoff() {
return coff;
}
/**
* Return the name of this section.
*
* @return the name of this section.
*/
public String getName() {
return name;
}
/**
* Test whether this section is read-only.
*
* @return <tt>true</tt> if this section should never be written.
*/
public boolean isReadOnly() {
return readOnly;
}
/**
* Test whether this section is initialized. Loading a page from an
* initialized section requires a disk access, while loading a page from an
* uninitialized section requires only zero-filling the page.
*
* @return <tt>true</tt> if this section contains initialized data in the
* executable.
*/
public boolean isInitialzed() {
return initialized;
}
/**
* Return the length of this section in pages.
*
* @return the number of pages in this section.
*/
public int getLength() {
return numPages;
}
/**
* Return the first virtual page number used by this section.
*
* @return the first virtual page number used by this section.
*/
public int getFirstVPN() {
return firstVPN;
}
/**
* Load a page from this segment into physical memory.
*
* @param spn the page number within this segment.
* @param ppn the physical page to load into.
*/
public void loadPage(int spn, int ppn) {
Lib.assertTrue(file != null);
Lib.assertTrue(spn>=0 && spn<numPages);
Lib.assertTrue(ppn>=0 && ppn<Machine.processor().getNumPhysPages());
int pageSize = Processor.pageSize;
byte[] memory = Machine.processor().getMemory();
int paddr = ppn*pageSize;
int faddr = contentOffset + spn*pageSize;
int initlen;
if (!initialized)
initlen = 0;
else if (spn == numPages-1)
/** initlen = size % pageSize;
* Bug identified by Steven Schlansker 3/20/08
* Bug fix by Michael Rauser
*/
initlen = (size==pageSize) ? pageSize : (size%pageSize);
else
initlen = pageSize;
if (initlen > 0)
Lib.strictReadFile(file, faddr, memory, paddr, initlen);
Arrays.fill(memory, paddr+initlen, paddr+pageSize, (byte) 0);
}
/** The COFF object to which this section belongs. */
protected Coff coff;
/** The COFF name of this section. */
protected String name;
/** True if this section contains code. */
protected boolean executable;
/** True if this section is read-only. */
protected boolean readOnly;
/** True if this section contains initialized data. */
protected boolean initialized;
/** The number of virtual pages in this section. */
protected int numPages;
/** The first virtual page number used by this section. */
protected int firstVPN;
private OpenFile file;
private int contentOffset, size;
/** The length of a COFF section header. */
public static final int headerLength = 40;
private static final char dbgCoffSection = 'c';
}

283
machine/Config.java Normal file
View File

@ -0,0 +1,283 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import java.util.HashMap;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.io.StreamTokenizer;
/**
* Provides routines to access the Nachos configuration.
*/
public final class Config {
/**
* Load configuration information from the specified file. Must be called
* before the Nachos security manager is installed.
*
* @param fileName the name of the file containing the
* configuration to use.
*/
public static void load(String fileName) {
System.out.print(" config");
Lib.assertTrue(!loaded);
loaded = true;
configFile = fileName;
try {
config = new HashMap<String, String>();
File file = new File(configFile);
Reader reader = new FileReader(file);
StreamTokenizer s = new StreamTokenizer(reader);
s.resetSyntax();
s.whitespaceChars(0x00, 0x20);
s.wordChars(0x21, 0xFF);
s.eolIsSignificant(true);
s.commentChar('#');
s.quoteChar('"');
int line = 1;
s.nextToken();
while (true) {
if (s.ttype == StreamTokenizer.TT_EOF)
break;
if (s.ttype == StreamTokenizer.TT_EOL) {
line++;
s.nextToken();
continue;
}
if (s.ttype != StreamTokenizer.TT_WORD)
loadError(line);
String key = s.sval;
if (s.nextToken() != StreamTokenizer.TT_WORD ||
!s.sval.equals("="))
loadError(line);
if (s.nextToken() != StreamTokenizer.TT_WORD && s.ttype != '"')
loadError(line);
String value = s.sval;
// ignore everything after first string
while (s.nextToken() != StreamTokenizer.TT_EOL &&
s.ttype != StreamTokenizer.TT_EOF);
if (config.get(key) != null)
loadError(line);
config.put(key, value);
line++;
}
}
catch (Throwable e) {
System.err.println("Error loading " + configFile);
System.exit(1);
}
}
private static void loadError(int line) {
System.err.println("Error in " + configFile + " line " + line);
System.exit(1);
}
private static void configError(String message) {
System.err.println("");
System.err.println("Error in " + configFile + ": " + message);
System.exit(1);
}
/**
* Get the value of a key in <tt>nachos.conf</tt>.
*
* @param key the key to look up.
* @return the value of the specified key, or <tt>null</tt> if it is not
* present.
*/
public static String getString(String key) {
return (String) config.get(key);
}
/**
* Get the value of a key in <tt>nachos.conf</tt>, returning the specified
* default if the key does not exist.
*
* @param key the key to look up.
* @param defaultValue the value to return if the key does not exist.
* @return the value of the specified key, or <tt>defaultValue</tt> if it
* is not present.
*/
public static String getString(String key, String defaultValue) {
String result = getString(key);
if (result == null)
return defaultValue;
return result;
}
private static Integer requestInteger(String key) {
try {
String value = getString(key);
if (value == null)
return null;
return new Integer(value);
}
catch (NumberFormatException e) {
configError(key + " should be an integer");
Lib.assertNotReached();
return null;
}
}
/**
* Get the value of an integer key in <tt>nachos.conf</tt>.
*
* @param key the key to look up.
* @return the value of the specified key.
*/
public static int getInteger(String key) {
Integer result = requestInteger(key);
if (result == null)
configError("missing int " + key);
return result.intValue();
}
/**
* Get the value of an integer key in <tt>nachos.conf</tt>, returning the
* specified default if the key does not exist.
*
* @param key the key to look up.
* @param defaultValue the value to return if the key does not exist.
* @return the value of the specified key, or <tt>defaultValue</tt> if the
* key does not exist.
*/
public static int getInteger(String key, int defaultValue) {
Integer result = requestInteger(key);
if (result == null)
return defaultValue;
return result.intValue();
}
private static Double requestDouble(String key) {
try {
String value = getString(key);
if (value == null)
return null;
return new Double(value);
}
catch (NumberFormatException e) {
configError(key + " should be a double");
Lib.assertNotReached();
return null;
}
}
/**
* Get the value of a double key in <tt>nachos.conf</tt>.
*
* @param key the key to look up.
* @return the value of the specified key.
*/
public static double getDouble(String key) {
Double result = requestDouble(key);
if (result == null)
configError("missing double " + key);
return result.doubleValue();
}
/**
* Get the value of a double key in <tt>nachos.conf</tt>, returning the
* specified default if the key does not exist.
*
* @param key the key to look up.
* @param defaultValue the value to return if the key does not exist.
* @return the value of the specified key, or <tt>defaultValue</tt> if the
* key does not exist.
*/
public static double getDouble(String key, double defaultValue) {
Double result = requestDouble(key);
if (result == null)
return defaultValue;
return result.doubleValue();
}
private static Boolean requestBoolean(String key) {
String value = getString(key);
if (value == null)
return null;
if (value.equals("1") || value.toLowerCase().equals("true")) {
return Boolean.TRUE;
}
else if (value.equals("0") || value.toLowerCase().equals("false")) {
return Boolean.FALSE;
}
else {
configError(key + " should be a boolean");
Lib.assertNotReached();
return null;
}
}
/**
* Get the value of a boolean key in <tt>nachos.conf</tt>.
*
* @param key the key to look up.
* @return the value of the specified key.
*/
public static boolean getBoolean(String key) {
Boolean result = requestBoolean(key);
if (result == null)
configError("missing boolean " + key);
return result.booleanValue();
}
/**
* Get the value of a boolean key in <tt>nachos.conf</tt>, returning the
* specified default if the key does not exist.
*
* @param key the key to look up.
* @param defaultValue the value to return if the key does not exist.
* @return the value of the specified key, or <tt>defaultValue</tt> if the
* key does not exist.
*/
public static boolean getBoolean(String key, boolean defaultValue) {
Boolean result = requestBoolean(key);
if (result == null)
return defaultValue;
return result.booleanValue();
}
private static boolean loaded = false;
private static String configFile;
private static HashMap<String, String> config;
}

620
machine/ElevatorBank.java Normal file
View File

@ -0,0 +1,620 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import nachos.threads.KThread;
import nachos.threads.Semaphore;
import java.util.Vector;
import java.util.LinkedList;
import java.util.Iterator;
/**
* A bank of elevators.
*/
public final class ElevatorBank implements Runnable {
/** Indicates an elevator intends to move down. */
public static final int dirDown = -1;
/** Indicates an elevator intends not to move. */
public static final int dirNeither = 0;
/** Indicates an elevator intends to move up. */
public static final int dirUp = 1;
/**
* Allocate a new elevator bank.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
*/
public ElevatorBank(Privilege privilege) {
System.out.print(" elevators");
this.privilege = privilege;
simulationStarted = false;
}
/**
* Initialize this elevator bank with the specified number of elevators and
* the specified number of floors. The software elevator controller must
* also be specified. This elevator must not already be running a
* simulation.
*
* @param numElevators the number of elevators in the bank.
* @param numFloors the number of floors in the bank.
* @param controller the elevator controller.
*/
public void init(int numElevators, int numFloors,
ElevatorControllerInterface controller) {
Lib.assertTrue(!simulationStarted);
this.numElevators = numElevators;
this.numFloors = numFloors;
manager = new ElevatorManager(controller);
elevators = new ElevatorState[numElevators];
for (int i=0; i<numElevators; i++)
elevators[i] = new ElevatorState(0);
numRiders = 0;
ridersVector = new Vector<RiderControls>();
enableGui = false;
gui = null;
}
/**
* Add a rider to the simulation. This method must not be called after
* <tt>run()</tt> is called.
*
* @param rider the rider to add.
* @param floor the floor the rider will start on.
* @param stops the array to pass to the rider's <tt>initialize()</tt>
* method.
* @return the controls that will be given to the rider.
*/
public RiderControls addRider(RiderInterface rider,
int floor, int[] stops) {
Lib.assertTrue(!simulationStarted);
RiderControls controls = new RiderState(rider, floor, stops);
ridersVector.addElement(controls);
numRiders++;
return controls;
}
/**
* Create a GUI for this elevator bank.
*/
public void enableGui() {
Lib.assertTrue(!simulationStarted);
Lib.assertTrue(Config.getBoolean("ElevatorBank.allowElevatorGUI"));
enableGui = true;
}
/**
* Run a simulation. Initialize all elevators and riders, and then
* fork threads to each of their <tt>run()</tt> methods. Return when the
* simulation is finished.
*/
public void run() {
Lib.assertTrue(!simulationStarted);
simulationStarted = true;
riders = new RiderState[numRiders];
ridersVector.toArray(riders);
if (enableGui) {
privilege.doPrivileged(new Runnable() {
public void run() { initGui(); }
});
}
for (int i=0; i<numRiders; i++)
riders[i].initialize();
manager.initialize();
for (int i=0; i<numRiders; i++)
riders[i].run();
manager.run();
for (int i=0; i<numRiders; i++)
riders[i].join();
manager.join();
simulationStarted = false;
}
private void initGui() {
int[] numRidersPerFloor = new int[numFloors];
for (int floor=0; floor<numFloors; floor++)
numRidersPerFloor[floor] = 0;
for (int rider=0; rider<numRiders; rider++)
numRidersPerFloor[riders[rider].floor]++;
gui = new ElevatorGui(numFloors, numElevators, numRidersPerFloor);
}
/**
* Tests whether this module is working.
*/
public static void selfTest() {
new ElevatorTest().run();
}
void postRiderEvent(int event, int floor, int elevator) {
int direction = dirNeither;
if (elevator != -1) {
Lib.assertTrue(elevator >= 0 && elevator < numElevators);
direction = elevators[elevator].direction;
}
RiderEvent e = new RiderEvent(event, floor, elevator, direction);
for (int i=0; i<numRiders; i++) {
RiderState rider = riders[i];
if ((rider.inElevator && rider.elevator == e.elevator) ||
(!rider.inElevator && rider.floor == e.floor)) {
rider.events.add(e);
rider.schedule(1);
}
}
}
private class ElevatorManager implements ElevatorControls {
ElevatorManager(ElevatorControllerInterface controller) {
this.controller = controller;
interrupt = new Runnable() { public void run() { interrupt(); }};
}
public int getNumFloors() {
return numFloors;
}
public int getNumElevators() {
return numElevators;
}
public void setInterruptHandler(Runnable handler) {
this.handler = handler;
}
public void openDoors(int elevator) {
Lib.assertTrue(elevator >= 0 && elevator < numElevators);
postRiderEvent(RiderEvent.eventDoorsOpened,
elevators[elevator].openDoors(), elevator);
if (gui != null) {
if (elevators[elevator].direction == dirUp)
gui.clearUpButton(elevators[elevator].floor);
else if (elevators[elevator].direction == dirDown)
gui.clearDownButton(elevators[elevator].floor);
gui.openDoors(elevator);
}
}
public void closeDoors(int elevator) {
Lib.assertTrue(elevator >= 0 && elevator < numElevators);
postRiderEvent(RiderEvent.eventDoorsClosed,
elevators[elevator].closeDoors(), elevator);
if (gui != null)
gui.closeDoors(elevator);
}
public boolean moveTo(int floor, int elevator) {
Lib.assertTrue(floor >= 0 && floor < numFloors);
Lib.assertTrue(elevator >= 0 && elevator < numElevators);
if (!elevators[elevator].moveTo(floor))
return false;
schedule(Stats.ElevatorTicks);
return true;
}
public int getFloor(int elevator) {
Lib.assertTrue(elevator >= 0 && elevator < numElevators);
return elevators[elevator].floor;
}
public void setDirectionDisplay(int elevator, int direction) {
Lib.assertTrue(elevator >= 0 && elevator < numElevators);
elevators[elevator].direction = direction;
if (elevators[elevator].doorsOpen) {
postRiderEvent(RiderEvent.eventDirectionChanged,
elevators[elevator].floor, elevator);
}
if (gui != null) {
if (elevators[elevator].doorsOpen) {
if (direction == dirUp)
gui.clearUpButton(elevators[elevator].floor);
else if (direction == dirDown)
gui.clearDownButton(elevators[elevator].floor);
}
gui.setDirectionDisplay(elevator, direction);
}
}
public void finish() {
finished = true;
Lib.assertTrue(KThread.currentThread() == thread);
done.V();
KThread.finish();
}
public ElevatorEvent getNextEvent() {
if (events.isEmpty())
return null;
else
return (ElevatorEvent) events.removeFirst();
}
void schedule(int when) {
privilege.interrupt.schedule(when, "elevator", interrupt);
}
void postEvent(int event, int floor, int elevator, boolean schedule) {
events.add(new ElevatorEvent(event, floor, elevator));
if (schedule)
schedule(1);
}
void interrupt() {
for (int i=0; i<numElevators; i++) {
if (elevators[i].atNextFloor()) {
if (gui != null)
gui.elevatorMoved(elevators[i].floor, i);
if (elevators[i].atDestination()) {
postEvent(ElevatorEvent.eventElevatorArrived,
elevators[i].destination, i, false);
}
else {
elevators[i].nextETA += Stats.ElevatorTicks;
privilege.interrupt.schedule(Stats.ElevatorTicks,
"elevator",
interrupt);
}
}
}
if (!finished && !events.isEmpty() && handler != null)
handler.run();
}
void initialize() {
controller.initialize(this);
}
void run() {
thread = new KThread(controller);
thread.setName("elevator controller");
thread.fork();
}
void join() {
postEvent(ElevatorEvent.eventRidersDone, -1, -1, true);
done.P();
}
ElevatorControllerInterface controller;
Runnable interrupt;
KThread thread;
Runnable handler = null;
LinkedList<ElevatorEvent> events = new LinkedList<ElevatorEvent>();
Semaphore done = new Semaphore(0);
boolean finished = false;
}
private class ElevatorState {
ElevatorState(int floor) {
this.floor = floor;
destination = floor;
}
int openDoors() {
Lib.assertTrue(!doorsOpen && !moving);
doorsOpen = true;
return floor;
}
int closeDoors() {
Lib.assertTrue(doorsOpen);
doorsOpen = false;
return floor;
}
boolean moveTo(int newDestination) {
Lib.assertTrue(!doorsOpen);
if (!moving) {
// can't move to current floor
if (floor == newDestination)
return false;
destination = newDestination;
nextETA = Machine.timer().getTime() + Stats.ElevatorTicks;
moving = true;
return true;
}
else {
// moving, shouldn't be at destination
Lib.assertTrue(floor != destination);
// make sure it's ok to stop
if ((destination > floor && newDestination <= floor) ||
(destination < floor && newDestination >= floor))
return false;
destination = newDestination;
return true;
}
}
boolean enter(RiderState rider, int onFloor) {
Lib.assertTrue(!riders.contains(rider));
if (!doorsOpen || moving || onFloor != floor ||
riders.size() == maxRiders)
return false;
riders.addElement(rider);
return true;
}
boolean exit(RiderState rider, int onFloor) {
Lib.assertTrue(riders.contains(rider));
if (!doorsOpen || moving || onFloor != floor)
return false;
riders.removeElement(rider);
return true;
}
boolean atNextFloor() {
if (!moving || Machine.timer().getTime() < nextETA)
return false;
Lib.assertTrue(destination != floor);
if (destination > floor)
floor++;
else
floor--;
for (Iterator i=riders.iterator(); i.hasNext(); ) {
RiderState rider = (RiderState) i.next();
rider.floor = floor;
}
return true;
}
boolean atDestination() {
if (!moving || destination != floor)
return false;
moving = false;
return true;
}
static final int maxRiders = 4;
int floor, destination;
long nextETA;
boolean doorsOpen = false, moving = false;
int direction = dirNeither;
public Vector<RiderState> riders = new Vector<RiderState>();
}
private class RiderState implements RiderControls {
RiderState(RiderInterface rider, int floor, int[] stops) {
this.rider = rider;
this.floor = floor;
this.stops = stops;
interrupt = new Runnable() { public void run() { interrupt(); }};
}
public int getNumFloors() {
return numFloors;
}
public int getNumElevators() {
return numElevators;
}
public void setInterruptHandler(Runnable handler) {
this.handler = handler;
}
public int getFloor() {
return floor;
}
public int[] getFloors() {
int[] array = new int[floors.size()];
for (int i=0; i<array.length; i++)
array[i] = ((Integer) floors.elementAt(i)).intValue();
return array;
}
public int getDirectionDisplay(int elevator) {
Lib.assertTrue(elevator >= 0 && elevator < numElevators);
return elevators[elevator].direction;
}
public RiderEvent getNextEvent() {
if (events.isEmpty())
return null;
else
return (RiderEvent) events.removeFirst();
}
public boolean pressDirectionButton(boolean up) {
if (up)
return pressUpButton();
else
return pressDownButton();
}
public boolean pressUpButton() {
Lib.assertTrue(!inElevator && floor < numFloors-1);
for (int elevator=0; elevator<numElevators; elevator++) {
if (elevators[elevator].doorsOpen &&
elevators[elevator].direction == ElevatorBank.dirUp &&
elevators[elevator].floor == floor)
return false;
}
manager.postEvent(ElevatorEvent.eventUpButtonPressed,
floor, -1, true);
if (gui != null)
gui.pressUpButton(floor);
return true;
}
public boolean pressDownButton() {
Lib.assertTrue(!inElevator && floor > 0);
for (int elevator=0; elevator<numElevators; elevator++) {
if (elevators[elevator].doorsOpen &&
elevators[elevator].direction == ElevatorBank.dirDown &&
elevators[elevator].floor == floor)
return false;
}
manager.postEvent(ElevatorEvent.eventDownButtonPressed,
floor, -1, true);
if (gui != null)
gui.pressDownButton(floor);
return true;
}
public boolean enterElevator(int elevator) {
Lib.assertTrue(!inElevator &&
elevator >= 0 && elevator < numElevators);
if (!elevators[elevator].enter(this, floor))
return false;
if (gui != null)
gui.enterElevator(floor, elevator);
inElevator = true;
this.elevator = elevator;
return true;
}
public boolean pressFloorButton(int floor) {
Lib.assertTrue(inElevator && floor >= 0 && floor < numFloors);
if (elevators[elevator].doorsOpen &&
elevators[elevator].floor == floor)
return false;
manager.postEvent(ElevatorEvent.eventFloorButtonPressed,
floor, elevator, true);
if (gui != null)
gui.pressFloorButton(floor, elevator);
return true;
}
public boolean exitElevator(int floor) {
Lib.assertTrue(inElevator && floor >= 0 && floor < numFloors);
if (!elevators[elevator].exit(this, floor))
return false;
inElevator = false;
floors.add(new Integer(floor));
if (gui != null)
gui.exitElevator(floor, elevator);
return true;
}
public void finish() {
finished = true;
int[] floors = getFloors();
Lib.assertTrue(floors.length == stops.length);
for (int i=0; i<floors.length; i++)
Lib.assertTrue(floors[i] == stops[i]);
Lib.assertTrue(KThread.currentThread() == thread);
done.V();
KThread.finish();
}
void schedule(int when) {
privilege.interrupt.schedule(when, "rider", interrupt);
}
void interrupt() {
if (!finished && !events.isEmpty() && handler != null)
handler.run();
}
void initialize() {
rider.initialize(this, stops);
}
void run() {
thread = new KThread(rider);
thread.setName("rider");
thread.fork();
}
void join() {
done.P();
}
RiderInterface rider;
boolean inElevator = false, finished = false;
int floor, elevator;
int[] stops;
Runnable interrupt, handler = null;
LinkedList<RiderEvent> events = new LinkedList<RiderEvent>();
Vector<Integer> floors = new Vector<Integer>();
Semaphore done = new Semaphore(0);
KThread thread;
}
private int numFloors, numElevators;
private ElevatorManager manager;
private ElevatorState[] elevators;
private int numRiders;
private Vector<RiderControls> ridersVector;
private RiderState[] riders;
private boolean simulationStarted, enableGui;
private Privilege privilege;
private ElevatorGui gui;
}

View File

@ -0,0 +1,40 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* A controller for all the elevators in an elevator bank. The controller
* accesses the elevator bank through an instance of <tt>ElevatorControls</tt>.
*/
public interface ElevatorControllerInterface extends Runnable {
/**
* Initialize this elevator controller. The controller will access the
* elevator bank through <i>controls</i>. This constructor should return
* immediately after this controller is initialized, but not until the
* interupt handler is set. The controller will start receiving events
* after this method returns, but potentially before <tt>run()</tt> is
* called.
*
* @param controls the controller's interface to the elevator
* bank. The controller must not attempt to access
* the elevator bank in <i>any</i> other way.
*/
public void initialize(ElevatorControls controls);
/**
* Cause the controller to use the provided controls to receive and process
* requests from riders. This method should not return, but instead should
* call <tt>controls.finish()</tt> when the controller is finished.
*/
public void run();
/** The number of ticks doors should be held open before closing them. */
public static final int timeDoorsOpen = 500;
/** Indicates an elevator intends to move down. */
public static final int dirDown = -1;
/** Indicates an elevator intends not to move. */
public static final int dirNeither = 0;
/** Indicates an elevator intends to move up. */
public static final int dirUp = 1;
}

View File

@ -0,0 +1,96 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* A set of controls that can be used by an elevator controller.
*/
public interface ElevatorControls {
/**
* Return the number of floors in the elevator bank. If <i>n</i> is the
* number of floors in the bank, then the floors are numbered <i>0</i>
* (the ground floor) through <i>n - 1</i> (the top floor).
*
* @return the number of floors in the bank.
*/
public int getNumFloors();
/**
* Return the number of elevators in the elevator bank. If <i>n</i> is the
* number of elevators in the bank, then the elevators are numbered
* <i>0</i> through <i>n - 1</i>.
*
* @return the numbe rof elevators in the bank.
*/
public int getNumElevators();
/**
* Set the elevator interrupt handler. This handler will be called when an
* elevator event occurs, and when all the riders have reaced their
* destinations.
*
* @param handler the elevator interrupt handler.
*/
public void setInterruptHandler(Runnable handler);
/**
* Open an elevator's doors.
*
* @param elevator which elevator's doors to open.
*/
public void openDoors(int elevator);
/**
* Close an elevator's doors.
*
* @param elevator which elevator's doors to close.
*/
public void closeDoors(int elevator);
/**
* Move an elevator to another floor. The elevator's doors must be closed.
* If the elevator is already moving and cannot safely stop at the
* specified floor because it has already passed or is about to pass the
* floor, fails and returns <tt>false</tt>. If the elevator is already
* stopped at the specified floor, returns <tt>false</tt>.
*
* @param floor the floor to move to.
* @param elevator the elevator to move.
* @return <tt>true</tt> if the elevator's destination was changed.
*/
public boolean moveTo(int floor, int elevator);
/**
* Return the current location of the elevator. If the elevator is in
* motion, the returned value will be within one of the exact location.
*
* @param elevator the elevator to locate.
* @return the floor the elevator is on.
*/
public int getFloor(int elevator);
/**
* Set which direction the elevator bank will show for this elevator's
* display. The <i>direction</i> argument should be one of the <i>dir*</i>
* constants in the <tt>ElevatorBank</tt> class.
*
* @param elevator the elevator whose direction display to set.
* @param direction the direction to show (up, down, or neither).
*/
public void setDirectionDisplay(int elevator, int direction);
/**
* Call when the elevator controller is finished.
*/
public void finish();
/**
* Return the next event in the event queue. Note that there may be
* multiple events pending when an elevator interrupt occurs, so this
* method should be called repeatedly until it returns <tt>null</tt>.
*
* @return the next event, or <tt>null</tt> if no further events are
* currently pending.
*/
public ElevatorEvent getNextEvent();
}

View File

@ -0,0 +1,33 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* An event that affects elevator software.
*/
public final class ElevatorEvent {
public ElevatorEvent(int event, int floor, int elevator) {
this.event = event;
this.floor = floor;
this.elevator = elevator;
}
/** The event identifier. Refer to the <i>event*</i> constants. */
public final int event;
/** The floor pertaining to the event, or -1 if not applicable. */
public final int floor;
/** The elevator pertaining to the event, or -1 if not applicable. */
public final int elevator;
/** An up button was pressed. */
public static final int eventUpButtonPressed = 0;
/** A down button was pressed. */
public static final int eventDownButtonPressed = 1;
/** A floor button was pressed inside an elevator. */
public static final int eventFloorButtonPressed = 2;
/** An elevator has arrived and stopped at its destination floor. */
public static final int eventElevatorArrived = 3;
/** All riders have finished; the elevator controller should terminate. */
public static final int eventRidersDone = 4;
}

424
machine/ElevatorGui.java Normal file
View File

@ -0,0 +1,424 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import java.awt.Frame;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Panel;
import java.awt.ScrollPane;
import java.awt.Canvas;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Color;
/**
* A graphical visualization for the <tt>ElevatorBank</tt> class.
*/
public final class ElevatorGui extends Frame {
private final static int w=90, h=75;
private int numFloors, numElevators;
private ElevatorShaft[] elevators;
private Floor[] floors;
private int totalWidth, totalHeight;
ElevatorGui(int numFloors, int numElevators, int[] numRidersPerFloor) {
this.numFloors = numFloors;
this.numElevators = numElevators;
totalWidth = w*(numElevators+1);
totalHeight = h*numFloors;
setTitle("Elevator Bank");
Panel floorPanel = new Panel(new GridLayout(numFloors, 1, 0, 0));
floors = new Floor[numFloors];
for (int i=numFloors-1; i>=0; i--) {
floors[i] = new Floor(i, numRidersPerFloor[i]);
floorPanel.add(floors[i]);
}
Panel panel = new Panel(new GridLayout(1, numElevators+1, 0, 0));
panel.add(floorPanel);
elevators = new ElevatorShaft[numElevators];
for (int i=0; i<numElevators; i++) {
elevators[i] = new ElevatorShaft(i);
panel.add(elevators[i]);
}
add(panel);
pack();
setVisible(true);
repaint();
}
void openDoors(int elevator) {
elevators[elevator].openDoors();
}
void closeDoors(int elevator) {
elevators[elevator].closeDoors();
}
void setDirectionDisplay(int elevator, int direction) {
elevators[elevator].setDirectionDisplay(direction);
}
void pressUpButton(int floor) {
floors[floor].pressUpButton();
}
void clearUpButton(int floor) {
floors[floor].clearUpButton();
}
void pressDownButton(int floor) {
floors[floor].pressDownButton();
}
void clearDownButton(int floor) {
floors[floor].clearDownButton();
}
void enterElevator(int floor, int elevator) {
floors[floor].removeRider();
elevators[elevator].addRider();
}
void pressFloorButton(int floor, int elevator) {
elevators[elevator].pressFloorButton(floor);
}
void exitElevator(int floor, int elevator) {
elevators[elevator].removeRider();
floors[floor].addRider();
}
void elevatorMoved(int floor, int elevator) {
elevators[elevator].elevatorMoved(floor);
}
private void paintRider(Graphics g, int x, int y, int r) {
g.setColor(Color.yellow);
g.fillOval(x-r, y-r, 2*r, 2*r);
g.setColor(Color.black);
g.fillOval(x-r/2, y-r/2, r/3, r/3);
g.fillOval(x+r/4, y-r/2, r/3, r/3);
g.drawArc(x-r/2, y-r/2, r, r, 210, 120);
}
private void paintRiders(Graphics g, int x, int y, int w, int h, int n) {
int r = 8, t = 20;
int xn = w/t;
int yn = h/t;
int x0 = x + (w-xn*t)/2 + t/2;
int y0 = y + h - t/2;
for (int j=0; j<yn; j++) {
for (int i=0; i<xn; i++) {
if (n-- > 0)
paintRider(g, x0 + i*t, y0 - j*t, r);
}
}
}
private class Floor extends Canvas {
int floor, numRiders;
boolean upSet = false;
boolean downSet = false;
Floor(int floor, int numRiders) {
this.floor = floor;
this.numRiders = numRiders;
setBackground(Color.black);
}
public Dimension getPreferredSize() {
return new Dimension(w, h);
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public Dimension getMaximumSize() {
return getPreferredSize();
}
public void repaint() {
super.repaint();
if (TCB.isNachosThread()) {
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
}
}
}
void pressUpButton() {
if (!upSet) {
upSet = true;
repaint();
}
}
void pressDownButton() {
if (!downSet) {
downSet = true;
repaint();
}
}
void clearUpButton() {
if (upSet) {
upSet = false;
repaint();
}
}
void clearDownButton() {
if (downSet) {
downSet = false;
repaint();
}
}
void addRider() {
numRiders++;
repaint();
}
void removeRider() {
numRiders--;
repaint();
}
public void paint(Graphics g) {
g.setColor(Color.lightGray);
g.drawLine(0, 0, w, 0);
paintRiders(g, 0, 5, 3*w/4, h-10, numRiders);
paintButtons(g);
}
private void paintButtons(Graphics g) {
int s = 3*w/4;
int x1 = s+w/32;
int x2 = w-w/32;
int y1 = h/8;
int y2 = h-h/8;
g.setColor(Color.darkGray);
g.drawRect(x1, y1, x2-x1, y2-y1);
g.setColor(Color.lightGray);
g.fillRect(x1+1, y1+1, x2-x1-2, y2-y1-2);
int r = Math.min((x2-x1)/3, (y2-y1)/6);
int xc = (x1+x2)/2;
int yc1 = (y1+y2)/2 - (3*r/2);
int yc2 = (y1+y2)/2 + (3*r/2);
g.setColor(Color.red);
if (floor < numFloors-1) {
if (upSet)
g.fillOval(xc-r, yc1-r, 2*r, 2*r);
else
g.drawOval(xc-r, yc1-r, 2*r, 2*r);
}
if (floor > 0) {
if (downSet)
g.fillOval(xc-r, yc2-r, 2*r, 2*r);
else
g.drawOval(xc-r, yc2-r, 2*r, 2*r);
}
}
}
private class ElevatorShaft extends Canvas {
ElevatorShaft(int elevator) {
this.elevator = elevator;
floorsSet = new boolean[numFloors];
for (int i=0; i<numFloors; i++)
floorsSet[i] = false;
setBackground(Color.black);
}
public Dimension getPreferredSize() {
return new Dimension(w, h*numFloors);
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public Dimension getMaximumSize() {
return getPreferredSize();
}
private void repaintElevator() {
repaint(s, h*(numFloors-1-Math.max(floor, prevFloor)), w-2*s, h*2);
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
}
}
void openDoors() {
doorsOpen = true;
repaintElevator();
}
void closeDoors() {
doorsOpen = false;
repaintElevator();
}
void setDirectionDisplay(int direction) {
this.direction = direction;
repaintElevator();
}
void pressFloorButton(int floor) {
if (!floorsSet[floor]) {
floorsSet[floor] = true;
repaintElevator();
}
}
void elevatorMoved(int floor) {
prevFloor = this.floor;
this.floor = floor;
floorsSet[floor] = false;
repaintElevator();
}
void addRider() {
numRiders++;
repaintElevator();
}
void removeRider() {
numRiders--;
repaintElevator();
}
public void paint(Graphics g) {
g.setColor(Color.lightGray);
if (g.hitClip(0, 0, s, h*numFloors)) {
g.drawLine(0, 0, 0, h*numFloors);
g.drawLine(s-1, 0, s-1, h*numFloors);
for (int y=0; y<h*numFloors-s; y+=s)
g.drawLine(0, y, s-1, y+s-1);
}
if (g.hitClip(w-s, 0, s, h*numFloors)) {
g.drawLine(w-s, 0, w-s, h*numFloors);
g.drawLine(w-1, 0, w-1, h*numFloors);
for (int y=0; y<h*numFloors-s; y+=s)
g.drawLine(w-s, y, w-1, y+s-1);
}
// rectangle containing direction display area
Rectangle d = new Rectangle(s*3/2, h*(numFloors-1-floor),
w-3*s, w/3-s);
// unit of measurement in direction rect (12ux4u)
int u = d.width/12;
// draw elevator, fill riders
Rectangle e = new Rectangle(d.x, d.y+d.height,
d.width, h-d.height-u);
g.drawRect(e.x, e.y, e.width, e.height);
paintRiders(g, e.x, e.y, e.width, e.height, numRiders);
g.setColor(Color.lightGray);
// draw doors...
if (doorsOpen) {
g.drawLine(e.x+2*s, e.y, e.x+2*s, e.y+e.height);
for (int y=0; y<e.height-2*s; y+=2*s)
g.drawLine(e.x, e.y+y, e.x+2*s, e.y+y+2*s);
g.drawLine(e.x+e.width-2*s, e.y,
e.x+e.width-2*s, e.y+e.height);
for (int y=0; y<e.height-2*s; y+=2*s)
g.drawLine(e.x+e.width-2*s, e.y+y, e.x+e.width, e.y+y+2*s);
}
else {
for (int x=0; x<e.width; x+=2*s)
g.drawLine(e.x+x, e.y, e.x+x, e.y+e.height);
}
g.setColor(Color.yellow);
int[] xUp = { d.x + u*6, d.x + u*8, d.x + u*7 };
int[] yUp = { d.y + u*3, d.y + u*3, d.y + u*1 };
int[] xDown = { d.x + u*4, d.x + u*6, d.x + u*5 };
int[] yDown = { d.y + u*1, d.y + u*1, d.y + u*3 };
// draw arrows
if (direction == ElevatorBank.dirUp)
g.fillPolygon(xUp, yUp, 3);
else
g.drawPolygon(xUp, yUp, 3);
if (direction == ElevatorBank.dirDown)
g.fillPolygon(xDown, yDown, 3);
else
g.drawPolygon(xDown, yDown, 3);
}
private static final int s = 5;
private boolean doorsOpen = false;
private int floor = 0, prevFloor = 0, numRiders = 0;
private int direction = ElevatorBank.dirNeither;
private int elevator;
private boolean floorsSet[];
}
}

158
machine/ElevatorTest.java Normal file
View File

@ -0,0 +1,158 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import nachos.threads.KThread;
import nachos.threads.Semaphore;
/**
* Tests the <tt>ElevatorBank</tt> module, using a single elevator and a single
* rider.
*/
public final class ElevatorTest {
/**
* Allocate a new <tt>ElevatorTest</tt> object.
*/
public ElevatorTest() {
}
/**
* Run a test on <tt>Machine.bank()</tt>.
*/
public void run() {
Machine.bank().init(1, 2, new ElevatorController());
int[] stops = { 1 };
Machine.bank().addRider(new Rider(), 0, stops);
Machine.bank().run();
}
private class ElevatorController implements ElevatorControllerInterface {
public void initialize(ElevatorControls controls) {
this.controls = controls;
eventWait = new Semaphore(0);
controls.setInterruptHandler(new Runnable() {
public void run() { interrupt(); }
});
}
public void run() {
ElevatorEvent e;
Lib.assertTrue(controls.getFloor(0) == 0);
e = getNextEvent();
Lib.assertTrue(e.event == ElevatorEvent.eventUpButtonPressed &&
e.floor == 0);
controls.setDirectionDisplay(0, dirUp);
controls.openDoors(0);
e = getNextEvent();
Lib.assertTrue(e.event == ElevatorEvent.eventFloorButtonPressed &&
e.floor == 1);
controls.closeDoors(0);
controls.moveTo(1, 0);
e = getNextEvent();
Lib.assertTrue(e.event == ElevatorEvent.eventElevatorArrived &&
e.floor == 1 &&
e.elevator == 0);
controls.openDoors(0);
e = getNextEvent();
Lib.assertTrue(e.event == ElevatorEvent.eventRidersDone);
controls.finish();
Lib.assertNotReached();
}
private void interrupt() {
eventWait.V();
}
private ElevatorEvent getNextEvent() {
ElevatorEvent event;
while (true) {
if ((event = controls.getNextEvent()) != null)
break;
eventWait.P();
}
return event;
}
private ElevatorControls controls;
private Semaphore eventWait;
}
private class Rider implements RiderInterface {
public void initialize(RiderControls controls, int[] stops) {
this.controls = controls;
Lib.assertTrue(stops.length == 1 && stops[0] == 1);
eventWait = new Semaphore(0);
controls.setInterruptHandler(new Runnable() {
public void run() { interrupt(); }
});
}
public void run() {
RiderEvent e;
Lib.assertTrue(controls.getFloor() == 0);
controls.pressUpButton();
e = getNextEvent();
Lib.assertTrue(e.event == RiderEvent.eventDoorsOpened &&
e.floor == 0 &&
e.elevator == 0);
Lib.assertTrue(controls.getDirectionDisplay(0) == dirUp);
Lib.assertTrue(controls.enterElevator(0));
controls.pressFloorButton(1);
e = getNextEvent();
Lib.assertTrue(e.event == RiderEvent.eventDoorsClosed &&
e.floor == 0 &&
e.elevator == 0);
e = getNextEvent();
Lib.assertTrue(e.event == RiderEvent.eventDoorsOpened &&
e.floor == 1 &&
e.elevator == 0);
Lib.assertTrue(controls.exitElevator(1));
controls.finish();
Lib.assertNotReached();
}
private void interrupt() {
eventWait.V();
}
private RiderEvent getNextEvent() {
RiderEvent event;
while (true) {
if ((event = controls.getNextEvent()) != null)
break;
eventWait.P();
}
return event;
}
private RiderControls controls;
private Semaphore eventWait;
}
}

35
machine/FileSystem.java Normal file
View File

@ -0,0 +1,35 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* A file system that allows the user to create, open, and delete files.
*/
public interface FileSystem {
/**
* Atomically open a file, optionally creating it if it does not
* already exist. If the file does not
* already exist and <tt>create</tt> is <tt>false</tt>, returns
* <tt>null</tt>. If the file does not already exist and <tt>create</tt>
* is <tt>true</tt>, creates the file with zero length. If the file already
* exists, opens the file without changing it in any way.
*
* @param name the name of the file to open.
* @param create <tt>true</tt> to create the file if it does not
* already exist.
* @return an <tt>OpenFile</tt> representing a new instance of the opened
* file, or <tt>null</tt> if the file could not be opened.
*/
public OpenFile open(String name, boolean create);
/**
* Atomically remove an existing file. After a file is removed, it cannot
* be opened until it is created again with <tt>open</tt>. If the file is
* already open, it is up to the implementation to decide whether the file
* can still be accessed or if it is deleted immediately.
*
* @param name the name of the file to remove.
* @return <tt>true</tt> if the file was successfully removed.
*/
public boolean remove(String name);
}

253
machine/Interrupt.java Normal file
View File

@ -0,0 +1,253 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import java.util.TreeSet;
import java.util.Iterator;
import java.util.SortedSet;
/**
* The <tt>Interrupt</tt> class emulates low-level interrupt hardware. The
* hardware provides a method (<tt>setStatus()</tt>) to enable or disable
* interrupts.
*
* <p>
* In order to emulate the hardware, we need to keep track of all pending
* interrupts the hardware devices would cause, and when they are supposed to
* occur.
*
* <p>
* This module also keeps track of simulated time. Time advances only when the
* following occur:
* <ul>
* <li>interrupts are enabled, when they were previously disabled
* <li>a MIPS instruction is executed
* </ul>
*
* <p>
* As a result, unlike real hardware, interrupts (including time-slice context
* switches) cannot occur just anywhere in the code where interrupts are
* enabled, but rather only at those places in the code where simulated time
* advances (so that it becomes time for the hardware simulation to invoke an
* interrupt handler).
*
* <p>
* This means that incorrectly synchronized code may work fine on this hardware
* simulation (even with randomized time slices), but it wouldn't work on real
* hardware. But even though Nachos can't always detect when your program
* would fail in real life, you should still write properly synchronized code.
*/
public final class Interrupt {
/**
* Allocate a new interrupt controller.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
*/
public Interrupt(Privilege privilege) {
System.out.print(" interrupt");
this.privilege = privilege;
privilege.interrupt = new InterruptPrivilege();
enabled = false;
pending = new TreeSet<PendingInterrupt>();
}
/**
* Enable interrupts. This method has the same effect as
* <tt>setStatus(true)</tt>.
*/
public void enable() {
setStatus(true);
}
/**
* Disable interrupts and return the old interrupt state. This method has
* the same effect as <tt>setStatus(false)</tt>.
*
* @return <tt>true</tt> if interrupts were enabled.
*/
public boolean disable() {
return setStatus(false);
}
/**
* Restore interrupts to the specified status. This method has the same
* effect as <tt>setStatus(<i>status</i>)</tt>.
*
* @param status <tt>true</tt> to enable interrupts.
*/
public void restore(boolean status) {
setStatus(status);
}
/**
* Set the interrupt status to be enabled (<tt>true</tt>) or disabled
* (<tt>false</tt>) and return the previous status. If the interrupt
* status changes from disabled to enabled, the simulated time is advanced.
*
* @param status <tt>true</tt> to enable interrupts.
* @return <tt>true</tt> if interrupts were enabled.
*/
public boolean setStatus(boolean status) {
boolean oldStatus = enabled;
enabled = status;
if (oldStatus == false && status == true)
tick(true);
return oldStatus;
}
/**
* Tests whether interrupts are enabled.
*
* @return <tt>true</tt> if interrupts are enabled.
*/
public boolean enabled() {
return enabled;
}
/**
* Tests whether interrupts are disabled.
*
* @return <tt>true</tt> if interrupts are disabled.
*/
public boolean disabled() {
return !enabled;
}
private void schedule(long when, String type, Runnable handler) {
Lib.assertTrue(when>0);
long time = privilege.stats.totalTicks + when;
PendingInterrupt toOccur = new PendingInterrupt(time, type, handler);
Lib.debug(dbgInt,
"Scheduling the " + type +
" interrupt handler at time = " + time);
pending.add(toOccur);
}
private void tick(boolean inKernelMode) {
Stats stats = privilege.stats;
if (inKernelMode) {
stats.kernelTicks += Stats.KernelTick;
stats.totalTicks += Stats.KernelTick;
}
else {
stats.userTicks += Stats.UserTick;
stats.totalTicks += Stats.UserTick;
}
if (Lib.test(dbgInt))
System.out.println("== Tick " + stats.totalTicks + " ==");
enabled = false;
checkIfDue();
enabled = true;
}
private void checkIfDue() {
long time = privilege.stats.totalTicks;
Lib.assertTrue(disabled());
if (Lib.test(dbgInt))
print();
if (pending.isEmpty())
return;
if (((PendingInterrupt) pending.first()).time > time)
return;
Lib.debug(dbgInt, "Invoking interrupt handlers at time = " + time);
while (!pending.isEmpty() &&
((PendingInterrupt) pending.first()).time <= time) {
PendingInterrupt next = (PendingInterrupt) pending.first();
pending.remove(next);
Lib.assertTrue(next.time <= time);
if (privilege.processor != null)
privilege.processor.flushPipe();
Lib.debug(dbgInt, " " + next.type);
next.handler.run();
}
Lib.debug(dbgInt, " (end of list)");
}
private void print() {
System.out.println("Time: " + privilege.stats.totalTicks
+ ", interrupts " + (enabled ? "on" : "off"));
System.out.println("Pending interrupts:");
for (Iterator i=pending.iterator(); i.hasNext(); ) {
PendingInterrupt toOccur = (PendingInterrupt) i.next();
System.out.println(" " + toOccur.type +
", scheduled at " + toOccur.time);
}
System.out.println(" (end of list)");
}
private class PendingInterrupt implements Comparable {
PendingInterrupt(long time, String type, Runnable handler) {
this.time = time;
this.type = type;
this.handler = handler;
this.id = numPendingInterruptsCreated++;
}
public int compareTo(Object o) {
PendingInterrupt toOccur = (PendingInterrupt) o;
// can't return 0 for unequal objects, so check all fields
if (time < toOccur.time)
return -1;
else if (time > toOccur.time)
return 1;
else if (id < toOccur.id)
return -1;
else if (id > toOccur.id)
return 1;
else
return 0;
}
long time;
String type;
Runnable handler;
private long id;
}
private long numPendingInterruptsCreated = 0;
private Privilege privilege;
private boolean enabled;
private TreeSet<PendingInterrupt> pending;
private static final char dbgInt = 'i';
private class InterruptPrivilege implements Privilege.InterruptPrivilege {
public void schedule(long when, String type, Runnable handler) {
Interrupt.this.schedule(when, type, handler);
}
public void tick(boolean inKernelMode) {
Interrupt.this.tick(inKernelMode);
}
}
}

45
machine/Kernel.java Normal file
View File

@ -0,0 +1,45 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* An OS kernel.
*/
public abstract class Kernel {
/** Globally accessible reference to the kernel. */
public static Kernel kernel = null;
/**
* Allocate a new kernel.
*/
public Kernel() {
// make sure only one kernel is created
Lib.assertTrue(kernel == null);
kernel = this;
}
/**
* Initialize this kernel.
*/
public abstract void initialize(String[] args);
/**
* Test that this module works.
*
* <b>Warning:</b> this method will not be invoked by the autograder when
* we grade your projects. You should perform all initialization in
* <tt>initialize()</tt>.
*/
public abstract void selfTest();
/**
* Begin executing user programs, if applicable.
*/
public abstract void run();
/**
* Terminate this kernel. Never returns.
*/
public abstract void terminate();
}

638
machine/Lib.java Normal file
View File

@ -0,0 +1,638 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.PrivilegedAction;
import java.util.Random;
/**
* Thrown when an assertion fails.
*/
class AssertionFailureError extends Error {
AssertionFailureError() {
super();
}
AssertionFailureError(String message) {
super(message);
}
}
/**
* Provides miscellaneous library routines.
*/
public final class Lib {
/**
* Prevent instantiation.
*/
private Lib() {
}
private static Random random = null;
/**
* Seed the random number generater. May only be called once.
*
* @param randomSeed the seed for the random number generator.
*/
public static void seedRandom(long randomSeed) {
assertTrue(random == null);
random = new Random(randomSeed);
}
/**
* Return a random integer between 0 and <i>range - 1</i>. Must not be
* called before <tt>seedRandom()</tt> seeds the random number generator.
*
* @param range a positive value specifying the number of possible
* return values.
* @return a random integer in the specified range.
*/
public static int random(int range) {
assertTrue(range > 0);
return random.nextInt(range);
}
/**
* Return a random double between 0.0 (inclusive) and 1.0 (exclusive).
*
* @return a random double between 0.0 and 1.0.
*/
public static double random() {
return random.nextDouble();
}
/**
* Asserts that <i>expression</i> is <tt>true</tt>. If not, then Nachos
* exits with an error message.
*
* @param expression the expression to assert.
*/
public static void assertTrue(boolean expression) {
if (!expression)
throw new AssertionFailureError();
}
/**
* Asserts that <i>expression</i> is <tt>true</tt>. If not, then Nachos
* exits with the specified error message.
*
* @param expression the expression to assert.
* @param message the error message.
*/
public static void assertTrue(boolean expression, String message) {
if (!expression)
throw new AssertionFailureError(message);
}
/**
* Asserts that this call is never made. Same as <tt>assertTrue(false)</tt>.
*/
public static void assertNotReached() {
assertTrue(false);
}
/**
* Asserts that this call is never made, with the specified error messsage.
* Same as <tt>assertTrue(false, message)</tt>.
*
* @param message the error message.
*/
public static void assertNotReached(String message) {
assertTrue(false, message);
}
/**
* Print <i>message</i> if <i>flag</i> was enabled on the command line. To
* specify which flags to enable, use the -d command line option. For
* example, to enable flags a, c, and e, do the following:
*
* <p>
* <pre>nachos -d ace</pre>
*
* <p>
* Nachos uses several debugging flags already, but you are encouraged to
* add your own.
*
* @param flag the debug flag that must be set to print this message.
* @param message the debug message.
*/
public static void debug(char flag, String message) {
if (test(flag))
System.out.println(message);
}
/**
* Tests if <i>flag</i> was enabled on the command line.
*
* @param flag the debug flag to test.
*
* @return <tt>true</tt> if this flag was enabled on the command line.
*/
public static boolean test(char flag) {
if (debugFlags == null)
return false;
else if (debugFlags[(int) '+'])
return true;
else if (flag >= 0 && flag < 0x80 && debugFlags[(int) flag])
return true;
else
return false;
}
/**
* Enable all the debug flags in <i>flagsString</i>.
*
* @param flagsString the flags to enable.
*/
public static void enableDebugFlags(String flagsString) {
if (debugFlags == null)
debugFlags = new boolean[0x80];
char[] newFlags = flagsString.toCharArray();
for (int i=0; i<newFlags.length; i++) {
char c = newFlags[i];
if (c >= 0 && c < 0x80)
debugFlags[(int) c] = true;
}
}
/** Debug flags specified on the command line. */
private static boolean debugFlags[];
/**
* Read a file, verifying that the requested number of bytes is read, and
* verifying that the read operation took a non-zero amount of time.
*
* @param file the file to read.
* @param position the file offset at which to start reading.
* @param buf the buffer in which to store the data.
* @param offset the buffer offset at which storing begins.
* @param length the number of bytes to read.
*/
public static void strictReadFile(OpenFile file, int position,
byte[] buf, int offset, int length) {
long startTime = Machine.timer().getTime();
assertTrue(file.read(position, buf, offset, length) == length);
long finishTime = Machine.timer().getTime();
assertTrue(finishTime>startTime);
}
/**
* Load an entire file into memory.
*
* @param file the file to load.
* @return an array containing the contents of the entire file, or
* <tt>null</tt> if an error occurred.
*/
public static byte[] loadFile(OpenFile file) {
int startOffset = file.tell();
int length = file.length();
if (length < 0)
return null;
byte[] data = new byte[length];
file.seek(0);
int amount = file.read(data, 0, length);
file.seek(startOffset);
if (amount == length)
return data;
else
return null;
}
/**
* Take a read-only snapshot of a file.
*
* @param file the file to take a snapshot of.
* @return a read-only snapshot of the file.
*/
public static OpenFile cloneFile(OpenFile file) {
OpenFile clone = new ArrayFile(loadFile(file));
clone.seek(file.tell());
return clone;
}
/**
* Convert a short into its little-endian byte string representation.
*
* @param array the array in which to store the byte string.
* @param offset the offset in the array where the string will start.
* @param value the value to convert.
*/
public static void bytesFromShort(byte[] array, int offset, short value) {
array[offset+0] = (byte) ((value>>0)&0xFF);
array[offset+1] = (byte) ((value>>8)&0xFF);
}
/**
* Convert an int into its little-endian byte string representation.
*
* @param array the array in which to store the byte string.
* @param offset the offset in the array where the string will start.
* @param value the value to convert.
*/
public static void bytesFromInt(byte[] array, int offset, int value) {
array[offset+0] = (byte) ((value>>0) &0xFF);
array[offset+1] = (byte) ((value>>8) &0xFF);
array[offset+2] = (byte) ((value>>16)&0xFF);
array[offset+3] = (byte) ((value>>24)&0xFF);
}
/**
* Convert an int into its little-endian byte string representation, and
* return an array containing it.
*
* @param value the value to convert.
* @return an array containing the byte string.
*/
public static byte[] bytesFromInt(int value) {
byte[] array = new byte[4];
bytesFromInt(array, 0, value);
return array;
}
/**
* Convert an int into a little-endian byte string representation of the
* specified length.
*
* @param array the array in which to store the byte string.
* @param offset the offset in the array where the string will start.
* @param length the number of bytes to store (must be 1, 2, or 4).
* @param value the value to convert.
*/
public static void bytesFromInt(byte[] array, int offset,
int length, int value) {
assertTrue(length==1 || length==2 || length==4);
switch (length) {
case 1:
array[offset] = (byte) value;
break;
case 2:
bytesFromShort(array, offset, (short) value);
break;
case 4:
bytesFromInt(array, offset, value);
break;
}
}
/**
* Convert to a short from its little-endian byte string representation.
*
* @param array the array containing the byte string.
* @param offset the offset of the byte string in the array.
* @return the corresponding short value.
*/
public static short bytesToShort(byte[] array, int offset) {
return (short) ((((short) array[offset+0] & 0xFF) << 0) |
(((short) array[offset+1] & 0xFF) << 8));
}
/**
* Convert to an unsigned short from its little-endian byte string
* representation.
*
* @param array the array containing the byte string.
* @param offset the offset of the byte string in the array.
* @return the corresponding short value.
*/
public static int bytesToUnsignedShort(byte[] array, int offset) {
return (((int) bytesToShort(array, offset)) & 0xFFFF);
}
/**
* Convert to an int from its little-endian byte string representation.
*
* @param array the array containing the byte string.
* @param offset the offset of the byte string in the array.
* @return the corresponding int value.
*/
public static int bytesToInt(byte[] array, int offset) {
return (int) ((((int) array[offset+0] & 0xFF) << 0) |
(((int) array[offset+1] & 0xFF) << 8) |
(((int) array[offset+2] & 0xFF) << 16) |
(((int) array[offset+3] & 0xFF) << 24));
}
/**
* Convert to an int from a little-endian byte string representation of the
* specified length.
*
* @param array the array containing the byte string.
* @param offset the offset of the byte string in the array.
* @param length the length of the byte string.
* @return the corresponding value.
*/
public static int bytesToInt(byte[] array, int offset, int length) {
assertTrue(length==1 || length==2 || length==4);
switch (length) {
case 1:
return array[offset];
case 2:
return bytesToShort(array, offset);
case 4:
return bytesToInt(array, offset);
default:
return -1;
}
}
/**
* Convert to a string from a possibly null-terminated array of bytes.
*
* @param array the array containing the byte string.
* @param offset the offset of the byte string in the array.
* @param length the maximum length of the byte string.
* @return a string containing the specified bytes, up to and not
* including the null-terminator (if present).
*/
public static String bytesToString(byte[] array, int offset, int length) {
int i;
for (i=0; i<length; i++) {
if (array[offset+i] == 0)
break;
}
return new String(array, offset, i);
}
/** Mask out and shift a bit substring.
*
* @param bits the bit string.
* @param lowest the first bit of the substring within the string.
* @param size the number of bits in the substring.
* @return the substring.
*/
public static int extract(int bits, int lowest, int size) {
if (size == 32)
return (bits >> lowest);
else
return ((bits >> lowest) & ((1<<size)-1));
}
/** Mask out and shift a bit substring.
*
* @param bits the bit string.
* @param lowest the first bit of the substring within the string.
* @param size the number of bits in the substring.
* @return the substring.
*/
public static long extract(long bits, int lowest, int size) {
if (size == 64)
return (bits >> lowest);
else
return ((bits >> lowest) & ((1L<<size)-1));
}
/** Mask out and shift a bit substring; then sign extend the substring.
*
* @param bits the bit string.
* @param lowest the first bit of the substring within the string.
* @param size the number of bits in the substring.
* @return the substring, sign-extended.
*/
public static int extend(int bits, int lowest, int size) {
int extra = 32 - (lowest+size);
return ((extract(bits, lowest, size) << extra) >> extra);
}
/** Test if a bit is set in a bit string.
*
* @param flag the flag to test.
* @param bits the bit string.
* @return <tt>true</tt> if <tt>(bits & flag)</tt> is non-zero.
*/
public static boolean test(long flag, long bits) {
return ((bits & flag) != 0);
}
/**
* Creates a padded upper-case string representation of the integer
* argument in base 16.
*
* @param i an integer.
* @return a padded upper-case string representation in base 16.
*/
public static String toHexString(int i) {
return toHexString(i, 8);
}
/**
* Creates a padded upper-case string representation of the integer
* argument in base 16, padding to at most the specified number of digits.
*
* @param i an integer.
* @param pad the minimum number of hex digits to pad to.
* @return a padded upper-case string representation in base 16.
*/
public static String toHexString(int i, int pad) {
String result = Integer.toHexString(i).toUpperCase();
while (result.length() < pad)
result = "0" + result;
return result;
}
/**
* Divide two non-negative integers, round the quotient up to the nearest
* integer, and return it.
*
* @param a the numerator.
* @param b the denominator.
* @return <tt>ceiling(a / b)</tt>.
*/
public static int divRoundUp(int a, int b) {
assertTrue(a >= 0 && b > 0);
return ((a + (b-1)) / b);
}
/**
* Load and return the named class, or return <tt>null</tt> if the class
* could not be loaded.
*
* @param className the name of the class to load.
* @return the loaded class, or <tt>null</tt> if an error occurred.
*/
public static Class tryLoadClass(String className) {
try {
return ClassLoader.getSystemClassLoader().loadClass(className);
}
catch (Throwable e) {
return null;
}
}
/**
* Load and return the named class, terminating Nachos on any error.
*
* @param className the name of the class to load.
* @return the loaded class.
*/
public static Class loadClass(String className) {
try {
return ClassLoader.getSystemClassLoader().loadClass(className);
}
catch (Throwable e) {
Machine.terminate(e);
return null;
}
}
/**
* Create and return a new instance of the named class, using the
* constructor that takes no arguments.
*
* @param className the name of the class to instantiate.
* @return a new instance of the class.
*/
public static Object constructObject(String className) {
try {
// kamil - workaround for Java 1.4
// Thanks to Ka-Hing Cheung for the suggestion.
// Fixed for Java 1.5 by geels
Class[] param_types = new Class[0];
Object[] params = new Object[0];
return loadClass(className).getConstructor(param_types).newInstance(params);
}
catch (Throwable e) {
Machine.terminate(e);
return null;
}
}
/**
* Verify that the specified class extends or implements the specified
* superclass.
*
* @param cls the descendant class.
* @param superCls the ancestor class.
*/
public static void checkDerivation(Class<?> cls, Class<?> superCls) {
Lib.assertTrue(superCls.isAssignableFrom(cls));
}
/**
* Verifies that the specified class is public and not abstract, and that a
* constructor with the specified signature exists and is public.
*
* @param cls the class containing the constructor.
* @param parameterTypes the list of parameters.
*/
public static void checkConstructor(Class cls, Class[] parameterTypes) {
try {
Lib.assertTrue(Modifier.isPublic(cls.getModifiers()) &&
!Modifier.isAbstract(cls.getModifiers()));
Constructor constructor = cls.getConstructor(parameterTypes);
Lib.assertTrue(Modifier.isPublic(constructor.getModifiers()));
}
catch (Exception e) {
Lib.assertNotReached();
}
}
/**
* Verifies that the specified class is public, and that a non-static
* method with the specified name and signature exists, is public, and
* returns the specified type.
*
* @param cls the class containing the non-static method.
* @param methodName the name of the non-static method.
* @param parameterTypes the list of parameters.
* @param returnType the required return type.
*/
public static void checkMethod(Class cls, String methodName,
Class[] parameterTypes, Class returnType) {
try {
Lib.assertTrue(Modifier.isPublic(cls.getModifiers()));
Method method = cls.getMethod(methodName, parameterTypes);
Lib.assertTrue(Modifier.isPublic(method.getModifiers()) &&
!Modifier.isStatic(method.getModifiers()));
Lib.assertTrue(method.getReturnType() == returnType);
}
catch (Exception e) {
Lib.assertNotReached();
}
}
/**
* Verifies that the specified class is public, and that a static method
* with the specified name and signature exists, is public, and returns the
* specified type.
*
* @param cls the class containing the static method.
* @param methodName the name of the static method.
* @param parameterTypes the list of parameters.
* @param returnType the required return type.
*/
public static void checkStaticMethod(Class cls, String methodName,
Class[] parameterTypes,
Class returnType) {
try {
Lib.assertTrue(Modifier.isPublic(cls.getModifiers()));
Method method = cls.getMethod(methodName, parameterTypes);
Lib.assertTrue(Modifier.isPublic(method.getModifiers()) &&
Modifier.isStatic(method.getModifiers()));
Lib.assertTrue(method.getReturnType() == returnType);
}
catch (Exception e) {
Lib.assertNotReached();
}
}
/**
* Verifies that the specified class is public, and that a non-static field
* with the specified name and type exists, is public, and is not final.
*
* @param cls the class containing the field.
* @param fieldName the name of the field.
* @param fieldType the required type.
*/
public static void checkField(Class cls, String fieldName,
Class fieldType) {
try {
Lib.assertTrue(Modifier.isPublic(cls.getModifiers()));
Field field = cls.getField(fieldName);
Lib.assertTrue(field.getType() == fieldType);
Lib.assertTrue(Modifier.isPublic(field.getModifiers()) &&
!Modifier.isStatic(field.getModifiers()) &&
!Modifier.isFinal(field.getModifiers()));
}
catch (Exception e) {
Lib.assertNotReached();
}
}
/**
* Verifies that the specified class is public, and that a static field
* with the specified name and type exists and is public.
*
* @param cls the class containing the static field.
* @param fieldName the name of the static field.
* @param fieldType the required type.
*/
public static void checkStaticField(Class cls, String fieldName,
Class fieldType) {
try {
Lib.assertTrue(Modifier.isPublic(cls.getModifiers()));
Field field = cls.getField(fieldName);
Lib.assertTrue(field.getType() == fieldType);
Lib.assertTrue(Modifier.isPublic(field.getModifiers()) &&
Modifier.isStatic(field.getModifiers()));
}
catch (Exception e) {
Lib.assertNotReached();
}
}
}

489
machine/Machine.java Normal file
View File

@ -0,0 +1,489 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import nachos.ag.*;
import java.io.File;
/**
* The master class of the simulated machine. Processes command line arguments,
* constructs all simulated hardware devices, and starts the grader.
*/
public final class Machine {
/**
* Nachos main entry point.
*
* @param args the command line arguments.
*/
public static void main(final String[] args) {
System.out.print("nachos 5.0j initializing...");
Lib.assertTrue(Machine.args == null);
Machine.args = args;
processArgs();
Config.load(configFileName);
// get the current directory (.)
baseDirectory = new File(new File("").getAbsolutePath());
// get the nachos directory (./nachos)
nachosDirectory = new File(baseDirectory, "nachos");
String testDirectoryName =
Config.getString("FileSystem.testDirectory");
// get the test directory
if (testDirectoryName != null) {
testDirectory = new File(testDirectoryName);
}
else {
// use ../test
testDirectory = new File(baseDirectory.getParentFile(), "test");
}
securityManager = new NachosSecurityManager(testDirectory);
privilege = securityManager.getPrivilege();
privilege.machine = new MachinePrivilege();
TCB.givePrivilege(privilege);
privilege.stats = stats;
securityManager.enable();
createDevices();
checkUserClasses();
autoGrader = (AutoGrader) Lib.constructObject(autoGraderClassName);
new TCB().start(new Runnable() {
public void run() { autoGrader.start(privilege); }
});
}
/**
* Yield to non-Nachos threads. Use in non-preemptive JVM's to give
* non-Nachos threads a chance to run.
*/
public static void yield() {
Thread.yield();
}
/**
* Terminate Nachos. Same as <tt>TCB.die()</tt>.
*/
public static void terminate() {
TCB.die();
}
/**
* Terminate Nachos as the result of an unhandled exception or error.
*
* @param e the exception or error.
*/
public static void terminate(Throwable e) {
if (e instanceof ThreadDeath)
throw (ThreadDeath) e;
e.printStackTrace();
terminate();
}
/**
* Print stats, and terminate Nachos.
*/
public static void halt() {
System.out.print("Machine halting!\n\n");
stats.print();
terminate();
}
/**
* Return an array containing all command line arguments.
*
* @return the command line arguments passed to Nachos.
*/
public static String[] getCommandLineArguments() {
String[] result = new String[args.length];
System.arraycopy(args, 0, result, 0, args.length);
return result;
}
private static void processArgs() {
for (int i=0; i<args.length; ) {
String arg = args[i++];
if (arg.length() > 0 && arg.charAt(0) == '-') {
if (arg.equals("-d")) {
Lib.assertTrue(i < args.length, "switch without argument");
Lib.enableDebugFlags(args[i++]);
}
else if (arg.equals("-h")) {
System.out.print(help);
System.exit(1);
}
else if (arg.equals("-m")) {
Lib.assertTrue(i < args.length, "switch without argument");
try {
numPhysPages = Integer.parseInt(args[i++]);
}
catch (NumberFormatException e) {
Lib.assertNotReached("bad value for -m switch");
}
}
else if (arg.equals("-s")) {
Lib.assertTrue(i < args.length, "switch without argument");
try {
randomSeed = Long.parseLong(args[i++]);
}
catch (NumberFormatException e) {
Lib.assertNotReached("bad value for -s switch");
}
}
else if (arg.equals("-x")) {
Lib.assertTrue(i < args.length, "switch without argument");
shellProgramName = args[i++];
}
else if (arg.equals("-z")) {
System.out.print(copyright);
System.exit(1);
}
// these switches are reserved for the autograder
else if (arg.equals("-[]")) {
Lib.assertTrue(i < args.length, "switch without argument");
configFileName = args[i++];
}
else if (arg.equals("--")) {
Lib.assertTrue(i < args.length, "switch without argument");
autoGraderClassName = args[i++];
}
}
}
Lib.seedRandom(randomSeed);
}
private static void createDevices() {
interrupt = new Interrupt(privilege);
timer = new Timer(privilege);
if (Config.getBoolean("Machine.bank"))
bank = new ElevatorBank(privilege);
if (Config.getBoolean("Machine.processor")) {
if (numPhysPages == -1)
numPhysPages = Config.getInteger("Processor.numPhysPages");
processor = new Processor(privilege, numPhysPages);
}
if (Config.getBoolean("Machine.console"))
console = new StandardConsole(privilege);
if (Config.getBoolean("Machine.stubFileSystem"))
stubFileSystem = new StubFileSystem(privilege, testDirectory);
if (Config.getBoolean("Machine.networkLink"))
networkLink = new NetworkLink(privilege);
}
private static void checkUserClasses() {
System.out.print(" user-check");
Class aclsInt = (new int[0]).getClass();
Class clsObject = Lib.loadClass("java.lang.Object");
Class clsRunnable = Lib.loadClass("java.lang.Runnable");
Class clsString = Lib.loadClass("java.lang.String");
Class clsKernel = Lib.loadClass("nachos.machine.Kernel");
Class clsFileSystem = Lib.loadClass("nachos.machine.FileSystem");
Class clsRiderControls = Lib.loadClass("nachos.machine.RiderControls");
Class clsElevatorControls =
Lib.loadClass("nachos.machine.ElevatorControls");
Class clsRiderInterface =
Lib.loadClass("nachos.machine.RiderInterface");
Class clsElevatorControllerInterface =
Lib.loadClass("nachos.machine.ElevatorControllerInterface");
Class clsAlarm = Lib.loadClass("nachos.threads.Alarm");
Class clsThreadedKernel =
Lib.loadClass("nachos.threads.ThreadedKernel");
Class clsKThread = Lib.loadClass("nachos.threads.KThread");
Class clsCommunicator = Lib.loadClass("nachos.threads.Communicator");
Class clsSemaphore = Lib.loadClass("nachos.threads.Semaphore");
Class clsLock = Lib.loadClass("nachos.threads.Lock");
Class clsCondition = Lib.loadClass("nachos.threads.Condition");
Class clsCondition2 = Lib.loadClass("nachos.threads.Condition2");
Class clsRider = Lib.loadClass("nachos.threads.Rider");
Class clsElevatorController =
Lib.loadClass("nachos.threads.ElevatorController");
Lib.checkDerivation(clsThreadedKernel, clsKernel);
Lib.checkStaticField(clsThreadedKernel, "alarm", clsAlarm);
Lib.checkStaticField(clsThreadedKernel, "fileSystem", clsFileSystem);
Lib.checkMethod(clsAlarm, "waitUntil", new Class[] { long.class },
void.class);
Lib.checkConstructor(clsKThread, new Class[] { });
Lib.checkConstructor(clsKThread, new Class[] { clsRunnable });
Lib.checkStaticMethod(clsKThread, "currentThread", new Class[] {},
clsKThread);
Lib.checkStaticMethod(clsKThread, "finish", new Class[] {},
void.class);
Lib.checkStaticMethod(clsKThread, "yield", new Class[] {}, void.class);
Lib.checkStaticMethod(clsKThread, "sleep", new Class[] {}, void.class);
Lib.checkMethod(clsKThread, "setTarget", new Class[]{ clsRunnable },
clsKThread);
Lib.checkMethod(clsKThread, "setName", new Class[] { clsString },
clsKThread);
Lib.checkMethod(clsKThread, "getName", new Class[] { }, clsString);
Lib.checkMethod(clsKThread, "fork", new Class[] { }, void.class);
Lib.checkMethod(clsKThread, "ready", new Class[] { }, void.class);
Lib.checkMethod(clsKThread, "join", new Class[] { }, void.class);
Lib.checkField(clsKThread, "schedulingState", clsObject);
Lib.checkConstructor(clsCommunicator, new Class[] {});
Lib.checkMethod(clsCommunicator, "speak", new Class[] { int.class },
void.class);
Lib.checkMethod(clsCommunicator, "listen", new Class[] { }, int.class);
Lib.checkConstructor(clsSemaphore, new Class[] { int.class });
Lib.checkMethod(clsSemaphore, "P", new Class[] { }, void.class);
Lib.checkMethod(clsSemaphore, "V", new Class[] { }, void.class);
Lib.checkConstructor(clsLock, new Class[] { });
Lib.checkMethod(clsLock, "acquire", new Class[] { }, void.class);
Lib.checkMethod(clsLock, "release", new Class[] { }, void.class);
Lib.checkMethod(clsLock, "isHeldByCurrentThread", new Class[]{ },
boolean.class);
Lib.checkConstructor(clsCondition, new Class[] { clsLock });
Lib.checkConstructor(clsCondition2, new Class[] { clsLock });
Lib.checkMethod(clsCondition, "sleep", new Class[] { }, void.class);
Lib.checkMethod(clsCondition, "wake", new Class[] { }, void.class);
Lib.checkMethod(clsCondition, "wakeAll", new Class[] { }, void.class);
Lib.checkMethod(clsCondition2, "sleep", new Class[] { }, void.class);
Lib.checkMethod(clsCondition2, "wake", new Class[] { }, void.class);
Lib.checkMethod(clsCondition2, "wakeAll", new Class[] { }, void.class);
Lib.checkDerivation(clsRider, clsRiderInterface);
Lib.checkConstructor(clsRider, new Class[] { });
Lib.checkMethod(clsRider, "initialize",
new Class[] { clsRiderControls, aclsInt }, void.class);
Lib.checkDerivation(clsElevatorController,
clsElevatorControllerInterface);
Lib.checkConstructor(clsElevatorController, new Class[] { });
Lib.checkMethod(clsElevatorController, "initialize",
new Class[] { clsElevatorControls }, void.class);
}
/**
* Prevent instantiation.
*/
private Machine() {
}
/**
* Return the hardware interrupt manager.
*
* @return the hardware interrupt manager.
*/
public static Interrupt interrupt() { return interrupt; }
/**
* Return the hardware timer.
*
* @return the hardware timer.
*/
public static Timer timer() { return timer; }
/**
* Return the hardware elevator bank.
*
* @return the hardware elevator bank, or <tt>null</tt> if it is not
* present.
*/
public static ElevatorBank bank() { return bank; }
/**
* Return the MIPS processor.
*
* @return the MIPS processor, or <tt>null</tt> if it is not present.
*/
public static Processor processor() { return processor; }
/**
* Return the hardware console.
*
* @return the hardware console, or <tt>null</tt> if it is not present.
*/
public static SerialConsole console() { return console; }
/**
* Return the stub filesystem.
*
* @return the stub file system, or <tt>null</tt> if it is not present.
*/
public static FileSystem stubFileSystem() { return stubFileSystem; }
/**
* Return the network link.
*
* @return the network link, or <tt>null</tt> if it is not present.
*/
public static NetworkLink networkLink() { return networkLink; }
/**
* Return the autograder.
*
* @return the autograder.
*/
public static AutoGrader autoGrader() { return autoGrader; }
private static Interrupt interrupt = null;
private static Timer timer = null;
private static ElevatorBank bank = null;
private static Processor processor = null;
private static SerialConsole console = null;
private static FileSystem stubFileSystem = null;
private static NetworkLink networkLink = null;
private static AutoGrader autoGrader = null;
private static String autoGraderClassName = "nachos.ag.AutoGrader";
/**
* Return the name of the shell program that a user-programming kernel
* must run. Make sure <tt>UserKernel.run()</tt> <i>always</i> uses this
* method to decide which program to run.
*
* @return the name of the shell program to run.
*/
public static String getShellProgramName() {
if (shellProgramName == null)
shellProgramName = Config.getString("Kernel.shellProgram");
Lib.assertTrue(shellProgramName != null);
return shellProgramName;
}
private static String shellProgramName = null;
/**
* Return the name of the process class that the kernel should use. In
* the multi-programming project, returns
* <tt>nachos.userprog.UserProcess</tt>. In the VM project, returns
* <tt>nachos.vm.VMProcess</tt>. In the networking project, returns
* <tt>nachos.network.NetProcess</tt>.
*
* @return the name of the process class that the kernel should use.
*
* @see nachos.userprog.UserKernel#run
* @see nachos.userprog.UserProcess
* @see nachos.vm.VMProcess
* @see nachos.network.NetProcess
*/
public static String getProcessClassName() {
if (processClassName == null)
processClassName = Config.getString("Kernel.processClassName");
Lib.assertTrue(processClassName != null);
return processClassName;
}
private static String processClassName = null;
private static NachosSecurityManager securityManager;
private static Privilege privilege;
private static String[] args = null;
private static Stats stats = new Stats();
private static int numPhysPages = -1;
private static long randomSeed = 0;
private static File baseDirectory, nachosDirectory, testDirectory;
private static String configFileName = "nachos.conf";
private static final String help =
"\n" +
"Options:\n" +
"\n" +
"\t-d <debug flags>\n" +
"\t\tEnable some debug flags, e.g. -d ti\n" +
"\n" +
"\t-h\n" +
"\t\tPrint this help message.\n" +
"\n" +
"\t-m <pages>\n" +
"\t\tSpecify how many physical pages of memory to simulate.\n" +
"\n" +
"\t-s <seed>\n" +
"\t\tSpecify the seed for the random number generator (seed is a\n" +
"\t\tlong).\n" +
"\n" +
"\t-x <program>\n" +
"\t\tSpecify a program that UserKernel.run() should execute,\n" +
"\t\tinstead of the value of the configuration variable\n" +
"\t\tKernel.shellProgram\n" +
"\n" +
"\t-z\n" +
"\t\tprint the copyright message\n" +
"\n" +
"\t-- <grader class>\n" +
"\t\tSpecify an autograder class to use, instead of\n" +
"\t\tnachos.ag.AutoGrader\n" +
"\n" +
"\t-# <grader arguments>\n" +
"\t\tSpecify the argument string to pass to the autograder.\n" +
"\n" +
"\t-[] <config file>\n" +
"\t\tSpecifiy a config file to use, instead of nachos.conf\n" +
""
;
private static final String copyright = "\n"
+ "Copyright 1992-2001 The Regents of the University of California.\n"
+ "All rights reserved.\n"
+ "\n"
+ "Permission to use, copy, modify, and distribute this software and\n"
+ "its documentation for any purpose, without fee, and without\n"
+ "written agreement is hereby granted, provided that the above\n"
+ "copyright notice and the following two paragraphs appear in all\n"
+ "copies of this software.\n"
+ "\n"
+ "IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY\n"
+ "PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL\n"
+ "DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS\n"
+ "DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN\n"
+ "ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+ "\n"
+ "THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY\n"
+ "WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
+ "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE\n"
+ "SOFTWARE PROVIDED HEREUNDER IS ON AN \"AS IS\" BASIS, AND THE\n"
+ "UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE\n"
+ "MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\n"
;
private static class MachinePrivilege
implements Privilege.MachinePrivilege {
public void setConsole(SerialConsole console) {
Machine.console = console;
}
}
// dummy variables to make javac smarter
private static Coff dummy1 = null;
}

View File

@ -0,0 +1,14 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* Thrown when a malformed packet is processed.
*/
public class MalformedPacketException extends Exception {
/**
* Allocate a new <tt>MalformedPacketException</tt>.
*/
public MalformedPacketException() {
}
}

329
machine/NetworkLink.java Normal file
View File

@ -0,0 +1,329 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.net.SocketException;
/**
* A full-duplex network link. Provides ordered, unreliable delivery of
* limited-size packets to other machines on the network. Packets are
* guaranteed to be uncorrupted as well.
*
* <p>
* Recall the general layering of network protocols:
* <ul>
* <li>Session/Transport
* <li>Network
* <li>Link
* <li>Physical
* </ul>
*
* <p>
* The physical layer provides a bit stream interface to the link layer. This
* layer is very hardware-dependent.
*
* <p>
* The link layer uses the physical layer to provide a packet interface to the
* network layer. The link layer generally provides unreliable delivery of
* limited-size packets, but guarantees that packets will not arrive out of
* order. Some links protect against packet corruption as well. The ethernet
* protocol is an example of a link layer.
*
* <p>
* The network layer exists to connect multiple networks together into an
* internet. The network layer provides globally unique addresses. Routers
* (a.k.a. gateways) move packets across networks at this layer. The network
* layer provides unordered, unreliable delivery of limited-size uncorrupted
* packets to any machine on the same internet. The most commonly used network
* layer protocol is IP (Internet Protocol), which is used to connect the
* Internet.
*
* <p>
* The session/transport layer provides a byte-stream interface to the
* application. This means that the transport layer must deliver uncorrupted
* bytes to the application, in the same order they were sent. Byte-streams
* must be connected and disconnected, and exist between ports, not machines.
*
* <p>
* This class provides a link layer abstraction. Since we do not allow
* different Nachos networks to communicate with one another, there is no need
* for a network layer in Nachos. This should simplify your design for the
* session/transport layer, since you can assume packets never arrive out of
* order.
*/
public class NetworkLink {
/**
* Allocate a new network link.
*
* <p>
* <tt>nachos.conf</tt> specifies the reliability of the network. The
* reliability, between 0 and 1, is the probability that any particular
* packet will not get dropped by the network.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
*/
public NetworkLink(Privilege privilege) {
System.out.print(" network");
this.privilege = privilege;
try {
localHost = InetAddress.getLocalHost();
}
catch (UnknownHostException e) {
localHost = null;
}
Lib.assertTrue(localHost != null);
reliability = Config.getDouble("NetworkLink.reliability");
Lib.assertTrue(reliability > 0 && reliability <= 1.0);
socket = null;
for (linkAddress=0;linkAddress<Packet.linkAddressLimit;linkAddress++) {
try {
socket = new DatagramSocket(portBase + linkAddress, localHost);
break;
}
catch (SocketException e) {
}
}
if (socket == null) {
System.out.println("");
System.out.println("Unable to acquire a link address!");
Lib.assertNotReached();
}
System.out.print("(" + linkAddress + ")");
receiveInterrupt = new Runnable() {
public void run() { receiveInterrupt(); }
};
sendInterrupt = new Runnable() {
public void run() { sendInterrupt(); }
};
scheduleReceiveInterrupt();
Thread receiveThread = new Thread(new Runnable() {
public void run() { receiveLoop(); }
});
receiveThread.start();
}
/**
* Returns the address of this network link.
*
* @return the address of this network link.
*/
public int getLinkAddress() {
return linkAddress;
}
/**
* Set this link's receive and send interrupt handlers.
*
* <p>
* The receive interrupt handler is called every time a packet arrives
* and can be read using <tt>receive()</tt>.
*
* <p>
* The send interrupt handler is called every time a packet sent with
* <tt>send()</tt> is finished being sent. This means that another
* packet can be sent.
*
* @param receiveInterruptHandler the callback to call when a packet
* arrives.
* @param sendInterruptHandler the callback to call when another
* packet can be sent.
*/
public void setInterruptHandlers(Runnable receiveInterruptHandler,
Runnable sendInterruptHandler) {
this.receiveInterruptHandler = receiveInterruptHandler;
this.sendInterruptHandler = sendInterruptHandler;
}
private void scheduleReceiveInterrupt() {
privilege.interrupt.schedule(Stats.NetworkTime, "network recv",
receiveInterrupt);
}
private synchronized void receiveInterrupt() {
Lib.assertTrue(incomingPacket == null);
if (incomingBytes != null) {
if (Machine.autoGrader().canReceivePacket(privilege)) {
try {
incomingPacket = new Packet(incomingBytes);
privilege.stats.numPacketsReceived++;
}
catch (MalformedPacketException e) {
}
}
incomingBytes = null;
notify();
if (incomingPacket == null)
scheduleReceiveInterrupt();
else if (receiveInterruptHandler != null)
receiveInterruptHandler.run();
}
else {
scheduleReceiveInterrupt();
}
}
/**
* Return the next packet received.
*
* @return the next packet received, or <tt>null</tt> if no packet is
* available.
*/
public Packet receive() {
Packet p = incomingPacket;
if (incomingPacket != null) {
incomingPacket = null;
scheduleReceiveInterrupt();
}
return p;
}
private void receiveLoop() {
while (true) {
synchronized(this) {
while (incomingBytes != null) {
try {
wait();
}
catch (InterruptedException e) {
}
}
}
byte[] packetBytes;
try {
byte[] buffer = new byte[Packet.maxPacketLength];
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
socket.receive(dp);
packetBytes = new byte[dp.getLength()];
System.arraycopy(buffer,0, packetBytes,0, packetBytes.length);
}
catch (IOException e) {
return;
}
synchronized(this) {
incomingBytes = packetBytes;
}
}
}
private void scheduleSendInterrupt() {
privilege.interrupt.schedule(Stats.NetworkTime, "network send",
sendInterrupt);
}
private void sendInterrupt() {
Lib.assertTrue(outgoingPacket != null);
// randomly drop packets, according to its reliability
if (Machine.autoGrader().canSendPacket(privilege) &&
Lib.random() <= reliability) {
// ok, no drop
privilege.doPrivileged(new Runnable() {
public void run() { sendPacket(); }
});
}
else {
outgoingPacket = null;
}
if (sendInterruptHandler != null)
sendInterruptHandler.run();
}
private void sendPacket() {
Packet p = outgoingPacket;
outgoingPacket = null;
try {
socket.send(new DatagramPacket(p.packetBytes, p.packetBytes.length,
localHost, portBase+p.dstLink));
privilege.stats.numPacketsSent++;
}
catch (IOException e) {
}
}
/**
* Send another packet. If a packet is already being sent, the result is
* not defined.
*
* @param pkt the packet to send.
*/
public void send(Packet pkt) {
if (outgoingPacket == null)
scheduleSendInterrupt();
outgoingPacket = pkt;
}
private static final int hash;
private static final int portBase;
/**
* The address of the network to which are attached all network links in
* this JVM. This is a hash on the account name of the JVM running this
* Nachos instance. It is used to help prevent packets from other users
* from accidentally interfering with this network.
*/
public static final byte networkID;
static {
hash = System.getProperty("user.name").hashCode();
portBase = 0x4E41 + Math.abs(hash%0x4E41);
networkID = (byte) (hash/0x4E41);
}
private Privilege privilege;
private Runnable receiveInterrupt;
private Runnable sendInterrupt;
private Runnable receiveInterruptHandler = null;
private Runnable sendInterruptHandler = null;
private InetAddress localHost;
private DatagramSocket socket;
private byte linkAddress;
private double reliability;
private byte[] incomingBytes = null;
private Packet incomingPacket = null;
private Packet outgoingPacket = null;
private boolean sendBusy = false;
}

141
machine/OpenFile.java Normal file
View File

@ -0,0 +1,141 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import java.io.EOFException;
/**
* A file that supports reading, writing, and seeking.
*/
public class OpenFile {
/**
* Allocate a new <tt>OpenFile</tt> object with the specified name on the
* specified file system.
*
* @param fileSystem the file system to which this file belongs.
* @param name the name of the file, on that file system.
*/
public OpenFile(FileSystem fileSystem, String name) {
this.fileSystem = fileSystem;
this.name = name;
}
/**
* Allocate a new unnamed <tt>OpenFile</tt> that is not associated with any
* file system.
*/
public OpenFile() {
this(null, "unnamed");
}
/**
* Get the file system to which this file belongs.
*
* @return the file system to which this file belongs.
*/
public FileSystem getFileSystem() {
return fileSystem;
}
/**
* Get the name of this open file.
*
* @return the name of this open file.
*/
public String getName() {
return name;
}
/**
* Read this file starting at the specified position and return the number
* of bytes successfully read. If no bytes were read because of a fatal
* error, returns -1
*
* @param pos the offset in the file at which to start reading.
* @param buf the buffer to store the bytes in.
* @param offset the offset in the buffer to start storing bytes.
* @param length the number of bytes to read.
* @return the actual number of bytes successfully read, or -1 on failure.
*/
public int read(int pos, byte[] buf, int offset, int length) {
return -1;
}
/**
* Write this file starting at the specified position and return the number
* of bytes successfully written. If no bytes were written because of a
* fatal error, returns -1.
*
* @param pos the offset in the file at which to start writing.
* @param buf the buffer to get the bytes from.
* @param offset the offset in the buffer to start getting.
* @param length the number of bytes to write.
* @return the actual number of bytes successfully written, or -1 on
* failure.
*/
public int write(int pos, byte[] buf, int offset, int length) {
return -1;
}
/**
* Get the length of this file.
*
* @return the length of this file, or -1 if this file has no length.
*/
public int length() {
return -1;
}
/**
* Close this file and release any associated system resources.
*/
public void close() {
}
/**
* Set the value of the current file pointer.
*/
public void seek(int pos) {
}
/**
* Get the value of the current file pointer, or -1 if this file has no
* pointer.
*/
public int tell() {
return -1;
}
/**
* Read this file starting at the current file pointer and return the
* number of bytes successfully read. Advances the file pointer by this
* amount. If no bytes could be* read because of a fatal error, returns -1.
*
* @param buf the buffer to store the bytes in.
* @param offset the offset in the buffer to start storing bytes.
* @param length the number of bytes to read.
* @return the actual number of bytes successfully read, or -1 on failure.
*/
public int read(byte[] buf, int offset, int length) {
return -1;
}
/**
* Write this file starting at the current file pointer and return the
* number of bytes successfully written. Advances the file pointer by this
* amount. If no bytes could be written because of a fatal error, returns
* -1.
*
* @param buf the buffer to get the bytes from.
* @param offset the offset in the buffer to start getting.
* @param length the number of bytes to write.
* @return the actual number of bytes successfully written, or -1 on
* failure.
*/
public int write(byte[] buf, int offset, int length) {
return -1;
}
private FileSystem fileSystem;
private String name;
}

View File

@ -0,0 +1,58 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* An <tt>OpenFile</tt> that maintains a current file position.
*/
public abstract class OpenFileWithPosition extends OpenFile {
/**
* Allocate a new <tt>OpenFileWithPosition</tt> with the specified name on
* the specified file system.
*
* @param fileSystem the file system to which this file belongs.
* @param name the name of the file, on that file system.
*/
public OpenFileWithPosition(FileSystem fileSystem, String name) {
super(fileSystem, name);
}
/**
* Allocate a new unnamed <tt>OpenFileWithPosition</tt> that is not
* associated with any file system.
*/
public OpenFileWithPosition() {
super();
}
public void seek(int position) {
this.position = position;
}
public int tell() {
return position;
}
public int read(byte[] buf, int offset, int length) {
int amount = read(position, buf, offset, length);
if (amount == -1)
return -1;
position += amount;
return amount;
}
public int write(byte[] buf, int offset, int length) {
int amount = write(position, buf, offset, length);
if (amount == -1)
return -1;
position += amount;
return amount;
}
/**
* The current value of the file pointer.
*/
protected int position = 0;
}

106
machine/Packet.java Normal file
View File

@ -0,0 +1,106 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* A link-layer packet.
*
* @see nachos.machine.NetworkLink
*/
public class Packet {
/**
* Allocate a new packet to be sent, using the specified parameters.
*
* @param dstLink the destination link address.
* @param srcLink the source link address.
* @param contents the contents of the packet.
*/
public Packet(int dstLink, int srcLink, byte[] contents)
throws MalformedPacketException {
// make sure the paramters are valid
if (dstLink < 0 || dstLink >= linkAddressLimit ||
srcLink < 0 || srcLink >= linkAddressLimit ||
contents.length > maxContentsLength)
throw new MalformedPacketException();
this.dstLink = dstLink;
this.srcLink = srcLink;
this.contents = contents;
packetBytes = new byte[headerLength + contents.length];
packetBytes[0] = NetworkLink.networkID;
packetBytes[1] = (byte) dstLink;
packetBytes[2] = (byte) srcLink;
packetBytes[3] = (byte) contents.length;
// if java had subarrays, i'd use them. but System.arraycopy is ok...
System.arraycopy(contents, 0, packetBytes, headerLength,
contents.length);
}
/**
* Allocate a new packet using the specified array of bytes received from
* the network.
*
* @param packetBytes the bytes making up this packet.
*/
public Packet(byte[] packetBytes) throws MalformedPacketException {
this.packetBytes = packetBytes;
// make sure we have a valid header
if (packetBytes.length < headerLength ||
packetBytes[0] != NetworkLink.networkID ||
packetBytes[1] < 0 || packetBytes[1] >= linkAddressLimit ||
packetBytes[2] < 0 || packetBytes[2] >= linkAddressLimit ||
packetBytes[3] < 0 || packetBytes[3] > packetBytes.length-4)
throw new MalformedPacketException();
dstLink = packetBytes[1];
srcLink = packetBytes[2];
contents = new byte[packetBytes[3]];
System.arraycopy(packetBytes, headerLength, contents, 0,
contents.length);
}
/** This packet, as an array of bytes that can be sent on a network. */
public byte[] packetBytes;
/** The address of the destination link of this packet. */
public int dstLink;
/** The address of the source link of this packet. */
public int srcLink;
/** The contents of this packet, excluding the link-layer header. */
public byte[] contents;
/**
* The number of bytes in a link-layer packet header. The header is
* formatted as follows:
*
* <table>
* <tr><td>offset</td><td>size</td><td>value</td></tr>
* <tr><td>0</td><td>1</td><td>network ID (collision detecting)</td></tr>
* <tr><td>1</td><td>1</td><td>destination link address</td></tr>
* <tr><td>2</td><td>1</td><td>source link address</td></tr>
* <tr><td>3</td><td>1</td><td>length of contents</td></tr>
* </table>
*/
public static final int headerLength = 4;
/**
* The maximum length, in bytes, of a packet that can be sent or received
* on the network.
*/
public static final int maxPacketLength = 32;
/**
* The maximum number of content bytes (not including the header). Note
* that this is just <tt>maxPacketLength - headerLength</tt>.
*/
public static final int maxContentsLength = maxPacketLength - headerLength;
/**
* The upper limit on Nachos link addresses. All link addresses fall
* between <tt>0</tt> and <tt>linkAddressLimit - 1</tt>.
*/
public static final int linkAddressLimit = 128;
}

1321
machine/Processor.java Normal file

File diff suppressed because it is too large Load Diff

142
machine/RiderControls.java Normal file
View File

@ -0,0 +1,142 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* A set of controls that can be used by a rider controller. Each rider uses a
* distinct <tt>RiderControls</tt> object.
*/
public interface RiderControls {
/**
* Return the number of floors in the elevator bank. If <i>n</i> is the
* number of floors in the bank, then the floors are numbered <i>0</i>
* (the ground floor) through <i>n - 1</i> (the top floor).
*
* @return the number of floors in the bank.
*/
public int getNumFloors();
/**
* Return the number of elevators in the elevator bank. If <i>n</i> is the
* number of elevators in the bank, then the elevators are numbered
* <i>0</i> through <i>n - 1</i>.
*
* @return the numbe rof elevators in the bank.
*/
public int getNumElevators();
/**
* Set the rider's interrupt handler. This handler will be called when the
* rider observes an event.
*
* @param handler the rider's interrupt handler.
*/
public void setInterruptHandler(Runnable handler);
/**
* Return the current location of the rider. If the rider is in motion,
* the returned value will be within one of the exact location.
*
* @return the floor the rider is on.
*/
public int getFloor();
/**
* Return an array specifying the sequence of floors at which this rider
* has successfully exited an elevator. This array naturally does not
* count the floor the rider started on, nor does it count floors where
* the rider is in the elevator and does not exit.
*
* @return an array specifying the floors this rider has visited.
*/
public int[] getFloors();
/**
* Return the indicated direction of the specified elevator, set by
* <tt>ElevatorControls.setDirectionDisplay()</tt>.
*
* @param elevator the elevator to check the direction of.
* @return the displayed direction for the elevator.
*
* @see nachos.machine.ElevatorControls#setDirectionDisplay
*/
public int getDirectionDisplay(int elevator);
/**
* Press a direction button. If <tt>up</tt> is <tt>true</tt>, invoke
* <tt>pressUpButton()</tt>; otherwise, invoke <tt>pressDownButton()</tt>.
*
* @param up <tt>true</tt> to press the up button, <tt>false</tt> to
* press the down button.
* @return the return value of <tt>pressUpButton()</tt> or of
* <tt>pressDownButton()</tt>.
*/
public boolean pressDirectionButton(boolean up);
/**
* Press the up button. The rider must not be on the top floor and must not
* be inside an elevator. If an elevator is on the same floor as this
* rider, has the doors open, and says it is going up, does nothing and
* returns <tt>false</tt>.
*
* @return <tt>true</tt> if the button event was sent to the elevator
* controller.
*/
public boolean pressUpButton();
/**
* Press the down button. The rider must not be on the bottom floor and
* must not be inside an elevator. If an elevator is on the same floor as
* as this rider, has the doors open, and says it is going down, does
* nothing and returns <tt>false</tt>.
*
* @return <tt>true</tt> if the button event was sent to the elevator
* controller.
*/
public boolean pressDownButton();
/**
* Enter an elevator. A rider cannot enter an elevator if its doors are not
* open at the same floor, or if the elevator is full. The rider must not
* already be in an elevator.
*
* @param elevator the elevator to enter.
* @return <tt>true</tt> if the rider successfully entered the elevator.
*/
public boolean enterElevator(int elevator);
/**
* Press a floor button. The rider must be inside an elevator. If the
* elevator already has its doors open on <tt>floor</tt>, does nothing and
* returns <tt>false</tt>.
*
* @param floor the button to press.
* @return <tt>true</tt> if the button event was sent to the elevator
* controller.
*/
public boolean pressFloorButton(int floor);
/**
* Exit the elevator. A rider cannot exit the elevator if its doors are not
* open on the requested floor. The rider must already be in an elevator.
*
* @param floor the floor to exit on.
* @return <tt>true</tt> if the rider successfully got off the elevator.
*/
public boolean exitElevator(int floor);
/**
* Call when the rider is finished.
*/
public void finish();
/**
* Return the next event in the event queue. Note that there may be
* multiple events pending when a rider interrupt occurs, so this
* method should be called repeatedly until it returns <tt>null</tt>.
*
* @return the next event, or <tt>null</tt> if no further events are
* currently pending.
*/
public RiderEvent getNextEvent();
}

33
machine/RiderEvent.java Normal file
View File

@ -0,0 +1,33 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* An event that affects rider software. If a rider is outside the elevators,
* it will only receive events on the same floor as the rider. If a rider is
* inside an elevator, it will only receive events pertaining to that elevator.
*/
public final class RiderEvent {
public RiderEvent(int event, int floor, int elevator, int direction) {
this.event = event;
this.floor = floor;
this.elevator = elevator;
this.direction = direction;
}
/** The event identifier. Refer to the <i>event*</i> constants. */
public final int event;
/** The floor pertaining to the event, or -1 if not applicable. */
public final int floor;
/** The elevator pertaining to the event, or -1 if not applicable. */
public final int elevator;
/** The direction display of the elevator (neither if not applicable). */
public final int direction;
/** An elevator's doors have opened. */
public static final int eventDoorsOpened = 0;
/** An elevator's doors were open and its direction display changed. */
public static final int eventDirectionChanged = 1;
/** An elevator's doors have closed. */
public static final int eventDoorsClosed = 2;
}

View File

@ -0,0 +1,51 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
/**
* A single rider. Each rider accesses the elevator bank through an
* instance of <tt>RiderControls</tt>.
*/
public interface RiderInterface extends Runnable {
/**
* Initialize this rider. The rider will access the elevator bank through
* <i>controls</i>, and the rider will make stops at different floors as
* specified in <i>stops</i>. This method should return immediately after
* this rider is initialized, but not until the interrupt handler is
* set. The rider will start receiving events after this method returns,
* potentially before <tt>run()</tt> is called.
*
* @param controls the rider's interface to the elevator bank. The
* rider must not attempt to access the elevator
* bank in <i>any</i> other way.
* @param stops an array of stops the rider should make; see
* below.
*/
public void initialize(RiderControls controls, int[] stops);
/**
* Cause the rider to use the provided controls to make the stops specified
* in the constructor. The rider should stop at each of the floors in
* <i>stops</i>, an array of floor numbers. The rider should <i>only</i>
* make the specified stops.
*
* <p>
* For example, suppose the rider uses <i>controls</i> to determine that
* it is initially on floor 1, and suppose the stops array contains two
* elements: { 0, 2 }. Then the rider should get on an elevator, get off
* on floor 0, get on an elevator, and get off on floor 2, pushing buttons
* as necessary.
*
* <p>
* This method should not return, but instead should call
* <tt>controls.finish()</tt> when the rider is finished.
*/
public void run();
/** Indicates an elevator intends to move down. */
public static final int dirDown = -1;
/** Indicates an elevator intends not to move. */
public static final int dirNeither = 0;
/** Indicates an elevator intends to move up. */
public static final int dirUp = 1;
}

View File

@ -0,0 +1,49 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
/**
* A serial console can be used to send and receive characters. Only one
* character may be sent at a time, and only one character may be received at a
* time.
*/
public interface SerialConsole {
/**
* Set this console's receive and send interrupt handlers.
*
* <p>
* The receive interrupt handler is called every time another byte arrives
* and can be read using <tt>readByte()</tt>.
*
* <p>
* The send interrupt handler is called every time a byte sent with
* <tt>writeByte()</tt> is finished being sent. This means that another
* byte can be sent.
*
* @param receiveInterruptHandler the callback to call when a byte
* arrives.
* @param sendInterruptHandler the callback to call when another byte
* can be sent.
*/
public void setInterruptHandlers(Runnable receiveInterruptHandler,
Runnable sendInterruptHandler);
/**
* Return the next unsigned byte received (in the range <tt>0</tt> through
* <tt>255</tt>).
*
* @return the next byte read, or -1 if no byte is available.
*/
public int readByte();
/**
* Send another byte. If a byte is already being sent, the result is not
* defined.
*
* @param value the byte to be sent (the upper 24 bits are ignored).
*/
public void writeByte(int value);
}

View File

@ -0,0 +1,161 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import java.io.IOException;
/**
* A text-based console that uses System.in and System.out.
*/
public class StandardConsole implements SerialConsole {
/**
* Allocate a new standard console.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
*/
public StandardConsole(Privilege privilege) {
System.out.print(" console");
this.privilege = privilege;
receiveInterrupt = new Runnable() {
public void run() { receiveInterrupt(); }
};
sendInterrupt = new Runnable() {
public void run() { sendInterrupt(); }
};
scheduleReceiveInterrupt();
}
public final void setInterruptHandlers(Runnable receiveInterruptHandler,
Runnable sendInterruptHandler) {
this.receiveInterruptHandler = receiveInterruptHandler;
this.sendInterruptHandler = sendInterruptHandler;
}
private void scheduleReceiveInterrupt() {
privilege.interrupt.schedule(Stats.ConsoleTime, "console read",
receiveInterrupt);
}
/**
* Attempt to read a byte from the object backing this console.
*
* @return the byte read, or -1 of no data is available.
*/
protected int in() {
try {
if (System.in.available() <= 0)
return -1;
return System.in.read();
}
catch (IOException e) {
return -1;
}
}
private int translateCharacter(int c) {
// translate win32 0x0D 0x0A sequence to single newline
if (c == 0x0A && prevCarriageReturn) {
prevCarriageReturn = false;
return -1;
}
prevCarriageReturn = (c == 0x0D);
// invalid if non-ASCII
if (c >= 0x80)
return -1;
// backspace characters
else if (c == 0x04 || c == 0x08 || c == 0x19 || c == 0x1B || c == 0x7F)
return '\b';
// if normal ASCII range, nothing to do
else if (c >= 0x20)
return c;
// newline characters
else if (c == 0x0A || c == 0x0D)
return '\n';
// everything else is invalid
else
return -1;
}
private void receiveInterrupt() {
Lib.assertTrue(incomingKey == -1);
incomingKey = translateCharacter(in());
if (incomingKey == -1) {
scheduleReceiveInterrupt();
}
else {
privilege.stats.numConsoleReads++;
if (receiveInterruptHandler != null)
receiveInterruptHandler.run();
}
}
public final int readByte() {
int key = incomingKey;
if (incomingKey != -1) {
incomingKey = -1;
scheduleReceiveInterrupt();
}
return key;
}
private void scheduleSendInterrupt() {
privilege.interrupt.schedule(Stats.ConsoleTime, "console write",
sendInterrupt);
}
/**
* Write a byte to the object backing this console.
*
* @param value the byte to write.
*/
protected void out(int value) {
System.out.write(value);
System.out.flush();
}
private void sendInterrupt() {
Lib.assertTrue(outgoingKey != -1);
out(outgoingKey);
outgoingKey = -1;
privilege.stats.numConsoleWrites++;
if (sendInterruptHandler != null)
sendInterruptHandler.run();
}
public final void writeByte(int value) {
if (outgoingKey == -1)
scheduleSendInterrupt();
outgoingKey = value&0xFF;
}
private Privilege privilege = null;
private Runnable receiveInterrupt;
private Runnable sendInterrupt;
private Runnable receiveInterruptHandler = null;
private Runnable sendInterruptHandler = null;
private int incomingKey = -1;
private int outgoingKey = -1;
private boolean prevCarriageReturn = false;
}

100
machine/Stats.java Normal file
View File

@ -0,0 +1,100 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.machine.*;
/**
* An object that maintains Nachos runtime statistics.
*/
public final class Stats {
/**
* Allocate a new statistics object.
*/
public Stats() {
}
/**
* Print out the statistics in this object.
*/
public void print() {
System.out.println("Ticks: total " + totalTicks
+ ", kernel " + kernelTicks
+ ", user " + userTicks);
System.out.println("Disk I/O: reads " + numDiskReads
+ ", writes " + numDiskWrites);
System.out.println("Console I/O: reads " + numConsoleReads
+ ", writes " + numConsoleWrites);
System.out.println("Paging: page faults " + numPageFaults
+ ", TLB misses " + numTLBMisses);
System.out.println("Network I/O: received " + numPacketsReceived
+ ", sent " + numPacketsSent);
}
/**
* The total amount of simulated time that has passed since Nachos
* started.
*/
public long totalTicks = 0;
/**
* The total amount of simulated time that Nachos has spent in kernel mode.
*/
public long kernelTicks = 0;
/**
* The total amount of simulated time that Nachos has spent in user mode.
*/
public long userTicks = 0;
/** The total number of sectors Nachos has read from the simulated disk.*/
public int numDiskReads = 0;
/** The total number of sectors Nachos has written to the simulated disk.*/
public int numDiskWrites = 0;
/** The total number of characters Nachos has read from the console. */
public int numConsoleReads = 0;
/** The total number of characters Nachos has written to the console. */
public int numConsoleWrites = 0;
/** The total number of page faults that have occurred. */
public int numPageFaults = 0;
/** The total number of TLB misses that have occurred. */
public int numTLBMisses = 0;
/** The total number of packets Nachos has sent to the network. */
public int numPacketsSent = 0;
/** The total number of packets Nachos has received from the network. */
public int numPacketsReceived = 0;
/**
* The amount to advance simulated time after each user instructions is
* executed.
*/
public static final int UserTick = 1;
/**
* The amount to advance simulated time after each interrupt enable.
*/
public static final int KernelTick = 10;
/**
* The amount of simulated time required to rotate the disk 360 degrees.
*/
public static final int RotationTime = 500;
/**
* The amount of simulated time required for the disk to seek.
*/
public static final int SeekTime = 500;
/**
* The amount of simulated time required for the console to handle a
* character.
*/
public static final int ConsoleTime = 100;
/**
* The amount of simulated time required for the network to handle a
* packet.
*/
public static final int NetworkTime = 100;
/**
* The mean amount of simulated time between timer interrupts.
*/
public static final int TimerTicks = 500;
/**
* The amount of simulated time required for an elevator to move a floor.
*/
public static final int ElevatorTicks = 2000;
}

216
machine/StubFileSystem.java Normal file
View File

@ -0,0 +1,216 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import nachos.threads.*;
import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;
/**
* This class implements a file system that redirects all requests to the host
* operating system's file system.
*/
public class StubFileSystem implements FileSystem {
/**
* Allocate a new stub file system.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
* @param directory the root directory of the stub file system.
*/
public StubFileSystem(Privilege privilege, File directory) {
this.privilege = privilege;
this.directory = directory;
}
public OpenFile open(String name, boolean truncate) {
if (!checkName(name))
return null;
delay();
try {
return new StubOpenFile(name, truncate);
}
catch (IOException e) {
return null;
}
}
public boolean remove(String name) {
if (!checkName(name))
return false;
delay();
FileRemover fr = new FileRemover(new File(directory, name));
privilege.doPrivileged(fr);
return fr.successful;
}
private class FileRemover implements Runnable {
public FileRemover(File f) {
this.f = f;
}
public void run() {
successful = f.delete();
}
public boolean successful = false;
private File f;
}
private void delay() {
long time = Machine.timer().getTime();
int amount = 1000;
ThreadedKernel.alarm.waitUntil(amount);
Lib.assertTrue(Machine.timer().getTime() >= time+amount);
}
private class StubOpenFile extends OpenFileWithPosition {
StubOpenFile(final String name, final boolean truncate)
throws IOException {
super(StubFileSystem.this, name);
final File f = new File(directory, name);
if (openCount == maxOpenFiles)
throw new IOException();
privilege.doPrivileged(new Runnable() {
public void run() { getRandomAccessFile(f, truncate); }
});
if (file == null)
throw new IOException();
open = true;
openCount++;
}
private void getRandomAccessFile(File f, boolean truncate) {
try {
if (!truncate && !f.exists())
return;
file = new RandomAccessFile(f, "rw");
if (truncate)
file.setLength(0);
}
catch (IOException e) {
}
}
public int read(int pos, byte[] buf, int offset, int length) {
if (!open)
return -1;
try {
delay();
file.seek(pos);
return Math.max(0, file.read(buf, offset, length));
}
catch (IOException e) {
return -1;
}
}
public int write(int pos, byte[] buf, int offset, int length) {
if (!open)
return -1;
try {
delay();
file.seek(pos);
file.write(buf, offset, length);
return length;
}
catch (IOException e) {
return -1;
}
}
public int length() {
try {
return (int) file.length();
}
catch (IOException e) {
return -1;
}
}
public void close() {
if (open) {
open = false;
openCount--;
}
try {
file.close();
}
catch (IOException e) {
}
}
private RandomAccessFile file = null;
private boolean open = false;
}
private int openCount = 0;
private static final int maxOpenFiles = 16;
private Privilege privilege;
private File directory;
private static boolean checkName(String name) {
char[] chars = name.toCharArray();
for (int i=0; i<chars.length; i++) {
if (chars[i] < 0 || chars[i] >= allowedFileNameCharacters.length)
return false;
if (!allowedFileNameCharacters[(int) chars[i]])
return false;
}
return true;
}
private static boolean[] allowedFileNameCharacters = new boolean[0x80];
private static void reject(char c) {
allowedFileNameCharacters[c] = false;
}
private static void allow(char c) {
allowedFileNameCharacters[c] = true;
}
private static void reject(char first, char last) {
for (char c=first; c<=last; c++)
allowedFileNameCharacters[c] = false;
}
private static void allow(char first, char last) {
for (char c=first; c<=last; c++)
allowedFileNameCharacters[c] = true;
}
static {
reject((char) 0x00, (char) 0x7F);
allow('A', 'Z');
allow('a', 'z');
allow('0', '9');
allow('-');
allow('_');
allow('.');
allow(',');
}
}

414
machine/TCB.java Normal file
View File

@ -0,0 +1,414 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
import nachos.threads.KThread;
import java.util.Vector;
import java.security.PrivilegedAction;
/**
* A TCB simulates the low-level details necessary to create, context-switch,
* and destroy Nachos threads. Each TCB controls an underlying JVM Thread
* object.
*
* <p>
* Do not use any methods in <tt>java.lang.Thread</tt>, as they are not
* compatible with the TCB API. Most <tt>Thread</tt> methods will either crash
* Nachos or have no useful effect.
*
* <p>
* Do not use the <i>synchronized</i> keyword <b>anywhere</b> in your code.
* It's against the rules, <i>and</i> it can easily deadlock nachos.
*/
public final class TCB {
/**
* Allocate a new TCB.
*/
public TCB() {
}
/**
* Give the TCB class the necessary privilege to create threads. This is
* necessary, because unlike other machine classes that need privilege, we
* want the kernel to be able to create TCB objects on its own.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
*/
public static void givePrivilege(Privilege privilege) {
TCB.privilege = privilege;
privilege.tcb = new TCBPrivilege();
}
/**
* Causes the thread represented by this TCB to begin execution. The
* specified target is run in the thread.
*/
public void start(Runnable target) {
/* We will not use synchronization here, because we're assuming that
* either this is the first call to start(), or we're being called in
* the context of another TCB. Since we only allow one TCB to run at a
* time, no synchronization is necessary.
*
* The only way this assumption could be broken is if one of our
* non-Nachos threads used the TCB code.
*/
/* Make sure this TCB has not already been started. If done is false,
* then destroy() has not yet set javaThread back to null, so we can
* use javaThread as a reliable indicator of whether or not start() has
* already been invoked.
*/
Lib.assertTrue(javaThread == null && !done);
/* Make sure there aren't too many running TCBs already. This
* limitation exists in an effort to prevent wild thread usage.
*/
Lib.assertTrue(runningThreads.size() < maxThreads);
isFirstTCB = (currentTCB == null);
/* Probably unnecessary sanity check: if this is not the first TCB, we
* make sure that the current thread is bound to the current TCB. This
* check can only fail if non-Nachos threads invoke start().
*/
if (!isFirstTCB)
Lib.assertTrue(currentTCB.javaThread == Thread.currentThread());
/* At this point all checks are complete, so we go ahead and start the
* TCB. Whether or not this is the first TCB, it gets added to
* runningThreads, and we save the target closure.
*/
runningThreads.add(this);
this.target = target;
if (!isFirstTCB) {
/* If this is not the first TCB, we have to make a new Java thread
* to run it. Creating Java threads is a privileged operation.
*/
tcbTarget = new Runnable() {
public void run() { threadroot(); }
};
privilege.doPrivileged(new Runnable() {
public void run() { javaThread = new Thread(tcbTarget); }
});
/* The Java thread hasn't yet started, but we need to get it
* blocking in yield(). We do this by temporarily turning off the
* current TCB, starting the new Java thread, and waiting for it
* to wake us up from threadroot(). Once the new TCB wakes us up,
* it's safe to context switch to the new TCB.
*/
currentTCB.running = false;
this.javaThread.start();
currentTCB.waitForInterrupt();
}
else {
/* This is the first TCB, so we don't need to make a new Java
* thread to run it; we just steal the current Java thread.
*/
javaThread = Thread.currentThread();
/* All we have to do now is invoke threadroot() directly. */
threadroot();
}
}
/**
* Return the TCB of the currently running thread.
*/
public static TCB currentTCB() {
return currentTCB;
}
/**
* Context switch between the current TCB and this TCB. This TCB will
* become the new current TCB. It is acceptable for this TCB to be the
* current TCB.
*/
public void contextSwitch() {
/* Probably unnecessary sanity check: we make sure that the current
* thread is bound to the current TCB. This check can only fail if
* non-Nachos threads invoke start().
*/
Lib.assertTrue(currentTCB.javaThread == Thread.currentThread());
// make sure AutoGrader.runningThread() called associateThread()
Lib.assertTrue(currentTCB.associated);
currentTCB.associated = false;
// can't switch from a TCB to itself
if (this == currentTCB)
return;
/* There are some synchronization concerns here. As soon as we wake up
* the next thread, we cannot assume anything about static variables,
* or about any TCB's state. Therefore, before waking up the next
* thread, we must latch the value of currentTCB, and set its running
* flag to false (so that, in case we get interrupted before we call
* yield(), the interrupt will set the running flag and yield() won't
* block).
*/
TCB previous = currentTCB;
previous.running = false;
this.interrupt();
previous.yield();
}
/**
* Destroy this TCB. This TCB must not be in use by the current thread.
* This TCB must also have been authorized to be destroyed by the
* autograder.
*/
public void destroy() {
// make sure the current TCB is correct
Lib.assertTrue(currentTCB != null &&
currentTCB.javaThread == Thread.currentThread());
// can't destroy current thread
Lib.assertTrue(this != currentTCB);
// thread must have started but not be destroyed yet
Lib.assertTrue(javaThread != null && !done);
// ensure AutoGrader.finishingCurrentThread() called authorizeDestroy()
Lib.assertTrue(nachosThread == toBeDestroyed);
toBeDestroyed = null;
this.done = true;
currentTCB.running = false;
this.interrupt();
currentTCB.waitForInterrupt();
this.javaThread = null;
}
/**
* Destroy all TCBs and exit Nachos. Same as <tt>Machine.terminate()</tt>.
*/
public static void die() {
privilege.exit(0);
}
/**
* Test if the current JVM thread belongs to a Nachos TCB. The AWT event
* dispatcher is an example of a non-Nachos thread.
*
* @return <tt>true</tt> if the current JVM thread is a Nachos thread.
*/
public static boolean isNachosThread() {
return (currentTCB != null &&
Thread.currentThread() == currentTCB.javaThread);
}
private void threadroot() {
// this should be running the current thread
Lib.assertTrue(javaThread == Thread.currentThread());
if (!isFirstTCB) {
/* start() is waiting for us to wake it up, signalling that it's OK
* to context switch to us. We leave the running flag false so that
* we'll still run if a context switch happens before we go to
* sleep. All we have to do is wake up the current TCB and then
* wait to get woken up by contextSwitch() or destroy().
*/
currentTCB.interrupt();
this.yield();
}
else {
/* start() called us directly, so we just need to initialize
* a couple things.
*/
currentTCB = this;
running = true;
}
try {
target.run();
// no way out of here without going throw one of the catch blocks
Lib.assertNotReached();
}
catch (ThreadDeath e) {
// make sure this TCB is being destroyed properly
if (!done) {
System.out.print("\nTCB terminated improperly!\n");
privilege.exit(1);
}
runningThreads.removeElement(this);
if (runningThreads.isEmpty())
privilege.exit(0);
}
catch (Throwable e) {
System.out.print("\n");
e.printStackTrace();
runningThreads.removeElement(this);
if (runningThreads.isEmpty())
privilege.exit(1);
else
die();
}
}
/**
* Invoked by threadroot() and by contextSwitch() when it is necessary to
* wait for another TCB to context switch to this TCB. Since this TCB
* might get destroyed instead, we check the <tt>done</tt> flag after
* waking up. If it is set, the TCB that woke us up is waiting for an
* acknowledgement in destroy(). Otherwise, we just set the current TCB to
* this TCB and return.
*/
private void yield() {
waitForInterrupt();
if (done) {
currentTCB.interrupt();
throw new ThreadDeath();
}
currentTCB = this;
}
/**
* Waits on the monitor bound to this TCB until its <tt>running</tt> flag
* is set to <tt>true</tt>. <tt>waitForInterrupt()</tt> is used whenever a
* TCB needs to go to wait for its turn to run. This includes the ping-pong
* process of starting and destroying TCBs, as well as in context switching
* from this TCB to another. We don't rely on <tt>currentTCB</tt>, since it
* is updated by <tt>contextSwitch()</tt> before we get called.
*/
private synchronized void waitForInterrupt() {
while (!running) {
try { wait(); }
catch (InterruptedException e) { }
}
}
/**
* Wake up this TCB by setting its <tt>running</tt> flag to <tt>true</tt>
* and signalling the monitor bound to it. Used in the ping-pong process of
* starting and destroying TCBs, as well as in context switching to this
* TCB.
*/
private synchronized void interrupt() {
running = true;
notify();
}
private void associateThread(KThread thread) {
// make sure AutoGrader.runningThread() gets called only once per
// context switch
Lib.assertTrue(!associated);
associated = true;
Lib.assertTrue(thread != null);
if (nachosThread != null)
Lib.assertTrue(thread == nachosThread);
else
nachosThread = thread;
}
private static void authorizeDestroy(KThread thread) {
// make sure AutoGrader.finishingThread() gets called only once per
// destroy
Lib.assertTrue(toBeDestroyed == null);
toBeDestroyed = thread;
}
/**
* The maximum number of started, non-destroyed TCB's that can be in
* existence.
*/
public static final int maxThreads = 250;
/**
* A reference to the currently running TCB. It is initialized to
* <tt>null</tt> when the <tt>TCB</tt> class is loaded, and then the first
* invocation of <tt>start(Runnable)</tt> assigns <tt>currentTCB</tt> a
* reference to the first TCB. After that, only <tt>yield()</tt> can
* change <tt>currentTCB</tt> to the current TCB, and only after
* <tt>waitForInterrupt()</tt> returns.
*
* <p>
* Note that <tt>currentTCB.javaThread</tt> will not be the current thread
* if the current thread is not bound to a TCB (this includes the threads
* created for the hardware simulation).
*/
private static TCB currentTCB = null;
/**
* A vector containing all <i>running</i> TCB objects. It is initialized to
* an empty vector when the <tt>TCB</tt> class is loaded. TCB objects are
* added only in <tt>start(Runnable)</tt>, which can only be invoked once
* on each TCB object. TCB objects are removed only in each of the
* <tt>catch</tt> clauses of <tt>threadroot()</tt>, one of which is always
* invoked on thread termination. The maximum number of threads in
* <tt>runningThreads</tt> is limited to <tt>maxThreads</tt> by
* <tt>start(Runnable)</tt>. If <tt>threadroot()</tt> drops the number of
* TCB objects in <tt>runningThreads</tt> to zero, Nachos exits, so once
* the first TCB is created, this vector is basically never empty.
*/
private static Vector<TCB> runningThreads = new Vector<TCB>();
private static Privilege privilege;
private static KThread toBeDestroyed = null;
/**
* <tt>true</tt> if and only if this TCB is the first TCB to start, the one
* started in <tt>Machine.main(String[])</tt>. Initialized by
* <tt>start(Runnable)</tt>, on the basis of whether <tt>currentTCB</tt>
* has been initialized.
*/
private boolean isFirstTCB;
/**
* A reference to the Java thread bound to this TCB. It is initially
* <tt>null</tt>, assigned to a Java thread in <tt>start(Runnable)</tt>,
* and set to <tt>null</tt> again in <tt>destroy()</tt>.
*/
private Thread javaThread = null;
/**
* <tt>true</tt> if and only if the Java thread bound to this TCB ought to
* be running. This is an entirely different condition from membership in
* <tt>runningThreads</tt>, which contains all TCB objects that have
* started and have not terminated. <tt>running</tt> is only <tt>true</tt>
* when the associated Java thread ought to run ASAP. When starting or
* destroying a TCB, this is temporarily true for a thread other than that
* of the current TCB.
*/
private boolean running = false;
/**
* Set to <tt>true</tt> by <tt>destroy()</tt>, so that when
* <tt>waitForInterrupt()</tt> returns in the doomed TCB, <tt>yield()</tt>
* will know that the current TCB is doomed.
*/
private boolean done = false;
private KThread nachosThread = null;
private boolean associated = false;
private Runnable target;
private Runnable tcbTarget;
private static class TCBPrivilege implements Privilege.TCBPrivilege {
public void associateThread(KThread thread) {
Lib.assertTrue(currentTCB != null);
currentTCB.associateThread(thread);
}
public void authorizeDestroy(KThread thread) {
TCB.authorizeDestroy(thread);
}
}
}

89
machine/Timer.java Normal file
View File

@ -0,0 +1,89 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.security.*;
/**
* A hardware timer generates a CPU timer interrupt approximately every 500
* clock ticks. This means that it can be used for implementing time-slicing,
* or for having a thread go to sleep for a specific period of time.
*
* The <tt>Timer</tt> class emulates a hardware timer by scheduling a timer
* interrupt to occur every time approximately 500 clock ticks pass. There is
* a small degree of randomness here, so interrupts do not occur exactly every
* 500 ticks.
*/
public final class Timer {
/**
* Allocate a new timer.
*
* @param privilege encapsulates privileged access to the Nachos
* machine.
*/
public Timer(Privilege privilege) {
System.out.print(" timer");
this.privilege = privilege;
timerInterrupt = new Runnable() {
public void run() { timerInterrupt(); }
};
autoGraderInterrupt = new Runnable() {
public void run() {
Machine.autoGrader().timerInterrupt(Timer.this.privilege,
lastTimerInterrupt);
}
};
scheduleInterrupt();
}
/**
* Set the callback to use as a timer interrupt handler. The timer
* interrupt handler will be called approximately every 500 clock ticks.
*
* @param handler the timer interrupt handler.
*/
public void setInterruptHandler(Runnable handler) {
this.handler = handler;
}
/**
* Get the current time.
*
* @return the number of clock ticks since Nachos started.
*/
public long getTime() {
return privilege.stats.totalTicks;
}
private void timerInterrupt() {
scheduleInterrupt();
scheduleAutoGraderInterrupt();
lastTimerInterrupt = getTime();
if (handler != null)
handler.run();
}
private void scheduleInterrupt() {
int delay = Stats.TimerTicks;
delay += Lib.random(delay/10) - (delay/20);
privilege.interrupt.schedule(delay, "timer", timerInterrupt);
}
private void scheduleAutoGraderInterrupt() {
privilege.interrupt.schedule(1, "timerAG", autoGraderInterrupt);
}
private long lastTimerInterrupt;
private Runnable timerInterrupt;
private Runnable autoGraderInterrupt;
private Privilege privilege;
private Runnable handler = null;
}

View File

@ -0,0 +1,81 @@
// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.
package nachos.machine;
import nachos.machine.*;
/**
* A single translation between a virtual page and a physical page.
*/
public final class TranslationEntry {
/**
* Allocate a new invalid translation entry.
*/
public TranslationEntry() {
valid = false;
}
/**
* Allocate a new translation entry with the specified initial state.
*
* @param vpn the virtual page numben.
* @param ppn the physical page number.
* @param valid the valid bit.
* @param readOnly the read-only bit.
* @param used the used bit.
* @param dirty the dirty bit.
*/
public TranslationEntry(int vpn, int ppn, boolean valid, boolean readOnly,
boolean used, boolean dirty) {
this.vpn = vpn;
this.ppn = ppn;
this.valid = valid;
this.readOnly = readOnly;
this.used = used;
this.dirty = dirty;
}
/**
* Allocate a new translation entry, copying the contents of an existing
* one.
*
* @param entry the translation entry to copy.
*/
public TranslationEntry(TranslationEntry entry) {
vpn = entry.vpn;
ppn = entry.ppn;
valid = entry.valid;
readOnly = entry.readOnly;
used = entry.used;
dirty = entry.dirty;
}
/** The virtual page number. */
public int vpn;
/** The physical page number. */
public int ppn;
/**
* If this flag is <tt>false</tt>, this translation entry is ignored.
*/
public boolean valid;
/**
* If this flag is <tt>true</tt>, the user pprogram is not allowed to
* modify the contents of this virtual page.
*/
public boolean readOnly;
/**
* This flag is set to <tt>true</tt> every time the page is read or written
* by a user program.
*/
public boolean used;
/**
* This flag is set to <tt>true</tt> every time the page is written by a
* user program.
*/
public boolean dirty;
}

3
machine/package.html Normal file
View File

@ -0,0 +1,3 @@
<body>
Provides classes that implement the Nachos simulated machine.
</body>

105
network/MailMessage.java Normal file
View File

@ -0,0 +1,105 @@
package nachos.network;
import nachos.machine.*;
/**
* A mail message. Includes a packet header, a mail header, and the actual
* payload.
*
* @see nachos.machine.Packet
*/
public class MailMessage {
/**
* Allocate a new mail message to be sent, using the specified parameters.
*
* @param dstLink the destination link address.
* @param dstPort the destination port.
* @param srcLink the source link address.
* @param srcPort the source port.
* @param contents the contents of the packet.
*/
public MailMessage(int dstLink, int dstPort, int srcLink, int srcPort,
byte[] contents) throws MalformedPacketException {
// make sure the paramters are valid
if (dstPort < 0 || dstPort >= portLimit ||
srcPort < 0 || srcPort >= portLimit ||
contents.length > maxContentsLength)
throw new MalformedPacketException();
this.dstPort = (byte) dstPort;
this.srcPort = (byte) srcPort;
this.contents = contents;
byte[] packetContents = new byte[headerLength + contents.length];
packetContents[0] = (byte) dstPort;
packetContents[1] = (byte) srcPort;
System.arraycopy(contents, 0, packetContents, headerLength,
contents.length);
packet = new Packet(dstLink, srcLink, packetContents);
}
/**
* Allocate a new mail message using the specified packet from the network.
*
* @param packet the packet containg the mail message.
*/
public MailMessage(Packet packet) throws MalformedPacketException {
this.packet = packet;
// make sure we have a valid header
if (packet.contents.length < headerLength ||
packet.contents[0] < 0 || packet.contents[0] >= portLimit ||
packet.contents[1] < 0 || packet.contents[1] >= portLimit)
throw new MalformedPacketException();
dstPort = packet.contents[0];
srcPort = packet.contents[1];
contents = new byte[packet.contents.length - headerLength];
System.arraycopy(packet.contents, headerLength, contents, 0,
contents.length);
}
/**
* Return a string representation of the message headers.
*/
public String toString() {
return "from (" + packet.srcLink + ":" + srcPort +
") to (" + packet.dstLink + ":" + dstPort +
"), " + contents.length + " bytes";
}
/** This message, as a packet that can be sent through a network link. */
public Packet packet;
/** The port used by this message on the destination machine. */
public int dstPort;
/** The port used by this message on the source machine. */
public int srcPort;
/** The contents of this message, excluding the mail message header. */
public byte[] contents;
/**
* The number of bytes in a mail header. The header is formatted as
* follows:
*
* <table>
* <tr><td>offset</td><td>size</td><td>value</td></tr>
* <tr><td>0</td><td>1</td><td>destination port</td></tr>
* <tr><td>1</td><td>1</td><td>source port</td></tr>
* </table>
*/
public static final int headerLength = 2;
/** Maximum payload (real data) that can be included in a single mesage. */
public static final int maxContentsLength =
Packet.maxContentsLength - headerLength;
/**
* The upper limit on mail ports. All ports fall between <tt>0</tt> and
* <tt>portLimit - 1</tt>.
*/
public static final int portLimit = 128;
}

123
network/NetKernel.java Normal file
View File

@ -0,0 +1,123 @@
package nachos.network;
import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;
import nachos.vm.*;
import nachos.network.*;
/**
* A kernel with network support.
*/
public class NetKernel extends VMKernel {
/**
* Allocate a new networking kernel.
*/
public NetKernel() {
super();
}
/**
* Initialize this kernel.
*/
public void initialize(String[] args) {
super.initialize(args);
postOffice = new PostOffice();
}
/**
* Test the network. Create a server thread that listens for pings on port
* 1 and sends replies. Then ping one or two hosts. Note that this test
* assumes that the network is reliable (i.e. that the network's
* reliability is 1.0).
*/
public void selfTest() {
super.selfTest();
KThread serverThread = new KThread(new Runnable() {
public void run() { pingServer(); }
});
serverThread.fork();
System.out.println("Press any key to start the network test...");
console.readByte(true);
int local = Machine.networkLink().getLinkAddress();
// ping this machine first
ping(local);
// if we're 0 or 1, ping the opposite
if (local <= 1)
ping(1-local);
}
private void ping(int dstLink) {
int srcLink = Machine.networkLink().getLinkAddress();
System.out.println("PING " + dstLink + " from " + srcLink);
long startTime = Machine.timer().getTime();
MailMessage ping;
try {
ping = new MailMessage(dstLink, 1,
Machine.networkLink().getLinkAddress(), 0,
new byte[0]);
}
catch (MalformedPacketException e) {
Lib.assertNotReached();
return;
}
postOffice.send(ping);
MailMessage ack = postOffice.receive(0);
long endTime = Machine.timer().getTime();
System.out.println("time=" + (endTime-startTime) + " ticks");
}
private void pingServer() {
while (true) {
MailMessage ping = postOffice.receive(1);
MailMessage ack;
try {
ack = new MailMessage(ping.packet.srcLink, ping.srcPort,
ping.packet.dstLink, ping.dstPort,
ping.contents);
}
catch (MalformedPacketException e) {
// should never happen...
continue;
}
postOffice.send(ack);
}
}
/**
* Start running user programs.
*/
public void run() {
super.run();
}
/**
* Terminate this kernel. Never returns.
*/
public void terminate() {
super.terminate();
}
private PostOffice postOffice;
// dummy variables to make javac smarter
private static NetProcess dummy1 = null;
}

46
network/NetProcess.java Normal file
View File

@ -0,0 +1,46 @@
package nachos.network;
import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;
import nachos.vm.*;
/**
* A <tt>VMProcess</tt> that supports networking syscalls.
*/
public class NetProcess extends VMProcess {
/**
* Allocate a new process.
*/
public NetProcess() {
super();
}
private static final int
syscallConnect = 11,
syscallAccept = 12;
/**
* Handle a syscall exception. Called by <tt>handleException()</tt>. The
* <i>syscall</i> argument identifies which syscall the user executed:
*
* <table>
* <tr><td>syscall#</td><td>syscall prototype</td></tr>
* <tr><td>11</td><td><tt>int connect(int host, int port);</tt></td></tr>
* <tr><td>12</td><td><tt>int accept(int port);</tt></td></tr>
* </table>
*
* @param syscall the syscall number.
* @param a0 the first syscall argument.
* @param a1 the second syscall argument.
* @param a2 the third syscall argument.
* @param a3 the fourth syscall argument.
* @return the value to be returned to the user.
*/
public int handleSyscall(int syscall, int a0, int a1, int a2, int a3) {
switch (syscall) {
default:
return super.handleSyscall(syscall, a0, a1, a2, a3);
}
}
}

134
network/PostOffice.java Normal file
View File

@ -0,0 +1,134 @@
package nachos.network;
import nachos.machine.*;
import nachos.threads.*;
/**
* A collection of message queues, one for each local port. A
* <tt>PostOffice</tt> interacts directly with the network hardware. Because
* of the network hardware, we are guaranteed that messages will never be
* corrupted, but they might get lost.
*
* <p>
* The post office uses a "postal worker" thread to wait for messages to arrive
* from the network and to place them in the appropriate queues. This cannot
* be done in the receive interrupt handler because each queue (implemented
* with a <tt>SynchList</tt>) is protected by a lock.
*/
public class PostOffice {
/**
* Allocate a new post office, using an array of <tt>SynchList</tt>s.
* Register the interrupt handlers with the network hardware and start the
* "postal worker" thread.
*/
public PostOffice() {
messageReceived = new Semaphore(0);
messageSent = new Semaphore(0);
sendLock = new Lock();
queues = new SynchList[MailMessage.portLimit];
for (int i=0; i<queues.length; i++)
queues[i] = new SynchList();
Runnable receiveHandler = new Runnable() {
public void run() { receiveInterrupt(); }
};
Runnable sendHandler = new Runnable() {
public void run() { sendInterrupt(); }
};
Machine.networkLink().setInterruptHandlers(receiveHandler,
sendHandler);
KThread t = new KThread(new Runnable() {
public void run() { postalDelivery(); }
});
t.fork();
}
/**
* Retrieve a message on the specified port, waiting if necessary.
*
* @param port the port on which to wait for a message.
*
* @return the message received.
*/
public MailMessage receive(int port) {
Lib.assertTrue(port >= 0 && port < queues.length);
Lib.debug(dbgNet, "waiting for mail on port " + port);
MailMessage mail = (MailMessage) queues[port].removeFirst();
if (Lib.test(dbgNet))
System.out.println("got mail on port " + port + ": " + mail);
return mail;
}
/**
* Wait for incoming messages, and then put them in the correct mailbox.
*/
private void postalDelivery() {
while (true) {
messageReceived.P();
Packet p = Machine.networkLink().receive();
MailMessage mail;
try {
mail = new MailMessage(p);
}
catch (MalformedPacketException e) {
continue;
}
if (Lib.test(dbgNet))
System.out.println("delivering mail to port " + mail.dstPort
+ ": " + mail);
// atomically add message to the mailbox and wake a waiting thread
queues[mail.dstPort].add(mail);
}
}
/**
* Called when a packet has arrived and can be dequeued from the network
* link.
*/
private void receiveInterrupt() {
messageReceived.V();
}
/**
* Send a message to a mailbox on a remote machine.
*/
public void send(MailMessage mail) {
if (Lib.test(dbgNet))
System.out.println("sending mail: " + mail);
sendLock.acquire();
Machine.networkLink().send(mail.packet);
messageSent.P();
sendLock.release();
}
/**
* Called when a packet has been sent and another can be queued to the
* network link. Note that this is called even if the previous packet was
* dropped.
*/
private void sendInterrupt() {
messageSent.V();
}
private SynchList[] queues;
private Semaphore messageReceived; // V'd when a message can be dequeued
private Semaphore messageSent; // V'd when a message can be queued
private Lock sendLock;
private static final char dbgNet = 'n';
}

3
network/package.html Normal file
View File

@ -0,0 +1,3 @@
<body>
Provides classes that allow Nachos processes to communicate over the network.
</body>

3
proj1/Makefile Normal file
View File

@ -0,0 +1,3 @@
DIRS = threads machine security ag
include ../Makefile

10
proj1/nachos.conf Normal file
View File

@ -0,0 +1,10 @@
Machine.stubFileSystem = false
Machine.processor = false
Machine.console = false
Machine.disk = false
Machine.bank = false
Machine.networkLink = false
ElevatorBank.allowElevatorGUI = true
NachosSecurityManager.fullySecure = false
ThreadedKernel.scheduler = nachos.threads.RoundRobinScheduler #nachos.threads.PriorityScheduler
Kernel.kernel = nachos.threads.ThreadedKernel

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More