Init
This commit is contained in:
commit
0f52953e3d
54
Makefile
Normal file
54
Makefile
Normal 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
270
README
Normal 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
279
ag/AutoGrader.java
Normal 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
81
ag/BoatGrader.java
Normal 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
3
ag/package.html
Normal file
@ -0,0 +1,3 @@
|
||||
<body>
|
||||
Provides classes that can be used to automatically grade Nachos projects.
|
||||
</body>
|
15
bin/nachos
Normal file
15
bin/nachos
Normal 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
44
machine/ArrayFile.java
Normal 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
151
machine/Coff.java
Normal 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
228
machine/CoffSection.java
Normal 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
283
machine/Config.java
Normal 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
620
machine/ElevatorBank.java
Normal 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;
|
||||
}
|
40
machine/ElevatorControllerInterface.java
Normal file
40
machine/ElevatorControllerInterface.java
Normal 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;
|
||||
}
|
96
machine/ElevatorControls.java
Normal file
96
machine/ElevatorControls.java
Normal 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();
|
||||
}
|
33
machine/ElevatorEvent.java
Normal file
33
machine/ElevatorEvent.java
Normal 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
424
machine/ElevatorGui.java
Normal 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
158
machine/ElevatorTest.java
Normal 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
35
machine/FileSystem.java
Normal 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
253
machine/Interrupt.java
Normal 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
45
machine/Kernel.java
Normal 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
638
machine/Lib.java
Normal 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
489
machine/Machine.java
Normal 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;
|
||||
}
|
14
machine/MalformedPacketException.java
Normal file
14
machine/MalformedPacketException.java
Normal 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
329
machine/NetworkLink.java
Normal 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
141
machine/OpenFile.java
Normal 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;
|
||||
}
|
58
machine/OpenFileWithPosition.java
Normal file
58
machine/OpenFileWithPosition.java
Normal 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
106
machine/Packet.java
Normal 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
1321
machine/Processor.java
Normal file
File diff suppressed because it is too large
Load Diff
142
machine/RiderControls.java
Normal file
142
machine/RiderControls.java
Normal 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
33
machine/RiderEvent.java
Normal 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;
|
||||
}
|
51
machine/RiderInterface.java
Normal file
51
machine/RiderInterface.java
Normal 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;
|
||||
}
|
49
machine/SerialConsole.java
Normal file
49
machine/SerialConsole.java
Normal 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);
|
||||
}
|
161
machine/StandardConsole.java
Normal file
161
machine/StandardConsole.java
Normal 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
100
machine/Stats.java
Normal 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
216
machine/StubFileSystem.java
Normal 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
414
machine/TCB.java
Normal 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
89
machine/Timer.java
Normal 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;
|
||||
}
|
81
machine/TranslationEntry.java
Normal file
81
machine/TranslationEntry.java
Normal 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
3
machine/package.html
Normal file
@ -0,0 +1,3 @@
|
||||
<body>
|
||||
Provides classes that implement the Nachos simulated machine.
|
||||
</body>
|
105
network/MailMessage.java
Normal file
105
network/MailMessage.java
Normal 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
123
network/NetKernel.java
Normal 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
46
network/NetProcess.java
Normal 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
134
network/PostOffice.java
Normal 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
3
network/package.html
Normal file
@ -0,0 +1,3 @@
|
||||
<body>
|
||||
Provides classes that allow Nachos processes to communicate over the network.
|
||||
</body>
|
3
proj1/Makefile
Normal file
3
proj1/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
DIRS = threads machine security ag
|
||||
|
||||
include ../Makefile
|
10
proj1/nachos.conf
Normal file
10
proj1/nachos.conf
Normal 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
|
3
proj2/Makefile
Normal file
3
proj2/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
DIRS = userprog threads machine security ag
|
||||
|
||||
include ../Makefile
|
14
proj2/nachos.conf
Normal file
14
proj2/nachos.conf
Normal file
@ -0,0 +1,14 @@
|
||||
Machine.stubFileSystem = true
|
||||
Machine.processor = true
|
||||
Machine.console = true
|
||||
Machine.disk = false
|
||||
Machine.bank = false
|
||||
Machine.networkLink = false
|
||||
Processor.usingTLB = false
|
||||
Processor.numPhysPages = 64
|
||||
ElevatorBank.allowElevatorGUI = false
|
||||
NachosSecurityManager.fullySecure = false
|
||||
ThreadedKernel.scheduler = nachos.threads.RoundRobinScheduler #nachos.threads.LotteryScheduler
|
||||
Kernel.shellProgram = halt.coff #sh.coff
|
||||
Kernel.processClassName = nachos.userprog.UserProcess
|
||||
Kernel.kernel = nachos.userprog.UserKernel
|
BIN
proj2/nachos/ag/AutoGrader.class
Normal file
BIN
proj2/nachos/ag/AutoGrader.class
Normal file
Binary file not shown.
BIN
proj2/nachos/ag/BoatGrader.class
Normal file
BIN
proj2/nachos/ag/BoatGrader.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ArrayFile.class
Normal file
BIN
proj2/nachos/machine/ArrayFile.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/AssertionFailureError.class
Normal file
BIN
proj2/nachos/machine/AssertionFailureError.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Coff.class
Normal file
BIN
proj2/nachos/machine/Coff.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/CoffSection.class
Normal file
BIN
proj2/nachos/machine/CoffSection.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Config.class
Normal file
BIN
proj2/nachos/machine/Config.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorBank$1.class
Normal file
BIN
proj2/nachos/machine/ElevatorBank$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorBank$ElevatorManager$1.class
Normal file
BIN
proj2/nachos/machine/ElevatorBank$ElevatorManager$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorBank$ElevatorManager.class
Normal file
BIN
proj2/nachos/machine/ElevatorBank$ElevatorManager.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorBank$ElevatorState.class
Normal file
BIN
proj2/nachos/machine/ElevatorBank$ElevatorState.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorBank$RiderState$1.class
Normal file
BIN
proj2/nachos/machine/ElevatorBank$RiderState$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorBank$RiderState.class
Normal file
BIN
proj2/nachos/machine/ElevatorBank$RiderState.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorBank.class
Normal file
BIN
proj2/nachos/machine/ElevatorBank.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorControllerInterface.class
Normal file
BIN
proj2/nachos/machine/ElevatorControllerInterface.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorControls.class
Normal file
BIN
proj2/nachos/machine/ElevatorControls.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorEvent.class
Normal file
BIN
proj2/nachos/machine/ElevatorEvent.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorGui$ElevatorShaft.class
Normal file
BIN
proj2/nachos/machine/ElevatorGui$ElevatorShaft.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorGui$Floor.class
Normal file
BIN
proj2/nachos/machine/ElevatorGui$Floor.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorGui.class
Normal file
BIN
proj2/nachos/machine/ElevatorGui.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorTest$1.class
Normal file
BIN
proj2/nachos/machine/ElevatorTest$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorTest$ElevatorController$1.class
Normal file
BIN
proj2/nachos/machine/ElevatorTest$ElevatorController$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorTest$ElevatorController.class
Normal file
BIN
proj2/nachos/machine/ElevatorTest$ElevatorController.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorTest$Rider$1.class
Normal file
BIN
proj2/nachos/machine/ElevatorTest$Rider$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorTest$Rider.class
Normal file
BIN
proj2/nachos/machine/ElevatorTest$Rider.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/ElevatorTest.class
Normal file
BIN
proj2/nachos/machine/ElevatorTest.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/FileSystem.class
Normal file
BIN
proj2/nachos/machine/FileSystem.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Interrupt$1.class
Normal file
BIN
proj2/nachos/machine/Interrupt$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Interrupt$InterruptPrivilege.class
Normal file
BIN
proj2/nachos/machine/Interrupt$InterruptPrivilege.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Interrupt$PendingInterrupt.class
Normal file
BIN
proj2/nachos/machine/Interrupt$PendingInterrupt.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Interrupt.class
Normal file
BIN
proj2/nachos/machine/Interrupt.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Kernel.class
Normal file
BIN
proj2/nachos/machine/Kernel.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Lib.class
Normal file
BIN
proj2/nachos/machine/Lib.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Machine$1.class
Normal file
BIN
proj2/nachos/machine/Machine$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Machine$MachinePrivilege.class
Normal file
BIN
proj2/nachos/machine/Machine$MachinePrivilege.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Machine.class
Normal file
BIN
proj2/nachos/machine/Machine.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/MalformedPacketException.class
Normal file
BIN
proj2/nachos/machine/MalformedPacketException.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/NetworkLink$1.class
Normal file
BIN
proj2/nachos/machine/NetworkLink$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/NetworkLink$2.class
Normal file
BIN
proj2/nachos/machine/NetworkLink$2.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/NetworkLink$3.class
Normal file
BIN
proj2/nachos/machine/NetworkLink$3.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/NetworkLink$4.class
Normal file
BIN
proj2/nachos/machine/NetworkLink$4.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/NetworkLink.class
Normal file
BIN
proj2/nachos/machine/NetworkLink.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/OpenFile.class
Normal file
BIN
proj2/nachos/machine/OpenFile.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/OpenFileWithPosition.class
Normal file
BIN
proj2/nachos/machine/OpenFileWithPosition.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Packet.class
Normal file
BIN
proj2/nachos/machine/Packet.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Processor$1.class
Normal file
BIN
proj2/nachos/machine/Processor$1.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Processor$Instruction.class
Normal file
BIN
proj2/nachos/machine/Processor$Instruction.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Processor$Mips.class
Normal file
BIN
proj2/nachos/machine/Processor$Mips.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Processor$MipsException.class
Normal file
BIN
proj2/nachos/machine/Processor$MipsException.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Processor$ProcessorPrivilege.class
Normal file
BIN
proj2/nachos/machine/Processor$ProcessorPrivilege.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/Processor.class
Normal file
BIN
proj2/nachos/machine/Processor.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/RiderControls.class
Normal file
BIN
proj2/nachos/machine/RiderControls.class
Normal file
Binary file not shown.
BIN
proj2/nachos/machine/RiderEvent.class
Normal file
BIN
proj2/nachos/machine/RiderEvent.class
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user