init: init nachos hw01

This commit is contained in:
AFS_TA
2026-03-27 10:12:37 +08:00
commit 1e698d2426
240 changed files with 76969 additions and 0 deletions

BIN
code/.DS_Store vendored Normal file

Binary file not shown.

12
code/README Executable file
View File

@@ -0,0 +1,12 @@
Building Instructions:
* got to the directory build.<host>, where <host> is your working OS
* do a "make depend" to build depenencies (DO IT!)
* do a "make" to build NachOS
Usage:
see "nachos -u" for all command line options
Building and starting user-level programs in NachOS:
* use Mips cross-compiler to build and link coff-binaries
* use coff2noff to translate the binaries to the NachOS-format
* start binary with nachos -x <path_to_file/file>

367
code/build.cygwin/Makefile Executable file
View File

@@ -0,0 +1,367 @@
# Copyright (c) 1992-1996 The Regents of the University of California.
# All rights reserved. See copyright.h for copyright notice and limitation
# of liability and disclaimer of warranty provisions.
#
# This is a GNU Makefile. It must be used with the GNU make program.
# At UW, the GNU make program is /software/gnu/bin/make.
# In many other places it is known as "gmake".
# You may wish to include /software/gnu/bin/ early in your command
# search path, so that you will be using GNU make when you type "make".
#
# About this Makefile:
# --------------------
#
# This Makefile is used to build the Nachos system, which includes
# the MIPS machine simulation and a simple operating system.
#
# There is a separate Makefile, in the "test" directory, that is
# used to build the Nachos test programs (which run on the
# simulated machine).
#
# There are several "build" directories, one for each type
# of machine in the MFCF computing environment
# (build.solaris, build.sunos, and build.ultrix), as well
# as a build directory for Linux (build.linux) and a generic
# build directory (build.other) for those who wish to try
# building Nachos on other platforms.
#
# This Makefile appears to be located in all of the build directories.
# If you edit it in one directory, the copies in all of the other
# directories appear to change as well. This is the desired behaviour,
# since this file is machine independent. (The file actually lives
# in build.solaris, with symbolic links from the other build directories.)
#
# The platform-dependent parts of make's instructions are located
# in the file Makefile.dep.
# There is a different Makefile.dep in each build directory.
#
# If you are in the MFCF environment, you should not have to edit
# the Makefile.dep files by hand. Any changes to the make instructions
# can be made in this file (see the instructions below) - they will
# apply no matter where you build Nachos.
# If you are not in the MFCF environment, e.g., if you are trying
# to build Nachos on Linux at home, you will probably need
# to edit Makefile.dep (in the appropriate build directory) to
# customize the make procedure to your environment.
#
# How to build Nachos for the first time:
# ---------------------------------------
#
# (1) Make sure than you are in the build directory for the
# type of machine you are logged in to (the "host" machine):
#
# host type examples build directory
# ----------- ----------- ----------------
#
# sparc/SunOS cayley,napier, build.sunos
# (SunOS 4.1.3) descartes
#
# sparc/Solaris picard.math, build.solaris
# (SunOS 5.x) hermite.math,
# markov.math,
# hypatia.math,
# hume.math
#
# mips/ULTRIX cantor.math build.ultrix
# (ULTRIX 4.2) noether.math
#
# If you are not sure what type of machine you are on,
# try the command "uname -a".
#
# (2) Type "make depend"
# - this computes file dependencies and records them
# at the end of the file Makefile.dep in
# your build directory. Have a look...
#
# (3) Type "make nachos" (or just "make").
# - make echos the commands it is executing, so that
# you can observe its progress. When the
# build is finished, you should have an
# executable "nachos" in the build directory.
#
# (4) There is no 4th step. You are done. Try running "./nachos -u".
#
#
# How to Re-build Nachos after you have changed the code:
#--------------------------------------------------------
#
# - The Nachos source code is located in the code subdirectories:
# threads, userprog, filesys, network, and lib. You may
# change the files in any of these directories, and you can
# add new files and/or remove files. The "machine" subdirectory
# contains the hardware simulation (which is also part of
# Nachos. You may look at it, but
# you may not change it, except as noted in machine/machine.h
# - When you want to re-make Nachos, always do it in the
# "build" directory that is appropriate for the machine
# type that you are running on.
# DO NOT TRY TO MAKE NACHOS IN THE SOURCE CODE DIRECTORIES.
#
# - IF all you have done is changed C++ code in existing files
# (since the last time you made Nachos in this build directory),
# THEN all you need to do to re-make Nachos is to type
#
# "make nachos"
#
# in the build directory.
#
# - IF you have done any of the following since the last build in
# this directory:
# added new .cc files or new .h files
# added or deleted #include's from existing files
# THEN
# you must do
# "make depend"
# followed by
# "make nachos"
#
# in the build directory.
#
# Note that is is always safe to do "make depend" followed by
# "make nachos", so if you are not sure what changes you have
# made, do "make depend".
#
# - IF you have added new files (.cc or .h) since the last build,
# you should edit this Makefile before running "make depend"
# and "make nachos".
# For new .h files, simply update the appropriate "_H" list below.
# For example, if you create a file called
# "bigfile.h" in the filesys subdirectory, you should add
# "../filesys/bigfile.h" to FILESYS_H, which is defined below
# For new .cc files, update the appropriate "_C" and "_O" lists.
# For example, if you create a file called "filetable.cc" in
# the directory "userprog", you should add
# "../userprog/filetable.cc" to USERPROG_C,
# and you should add "filetable.o" to USERPROG_O.
# Note that the entry in the "_C" list includes the subdirectory
# name, while the entry on the "_O" list does not.
#
# Some Important Notes:
# ---------------------
#
# * You can clean up all of the .o and other files left behind
# by make by typeing "make clean" in the build directory.
# * You can clean up .o and other files, as well as the nachos
# executable, DISK, core, SOCKET, and other files by typing
# make "distclean"
#
# These are good ways to save space, but the next build that
# you do after cleaning will take longer than usual, since
# much of the stuff you cleaned will need to be rebuilt.
#
# * When you build Nachos on an ULTRIX machine (in build.ultrix),
# you will get lots of warning messages like this:
#
# openfile.o: does not have gp tables for all it's sectons
#
# from the loader. Ignore them. Or better yet, figure out
# how to make them go away.
#
# The Most Important Note:
# -----------------------
#
# * If "make" is behaving strangely and you cannot figure out
# why, you should REBUILD the program FROM SCRATCH.
# Yes, it is slow.
# But, there are lots of little things that can go wrong, especially
# with all of these different types of machines available.
# Rebuilding from scratch at least gives you a known starting
# place. To rebuild from scratch, go to the appropriate
# build directory and do:
#
# make distclean
# make depend
# make nachos
#
################################################################
# READ THIS: CONFIGURING NACHOS
#
# Change DEFINES (below) to
# DEFINES = -DUSE_TLB -DFILESYS_STUB
# if you want the simulated machine to use its TLB
#
# If you want to use the real Nachos file system (based on
# the simulated disk), rather than the stub, remove
# the -DFILESYS_STUB from DEFINES.
#
# There is a a fix to the MIPS simulator to enable it to properly
# handle unaligned data access. This fix is enabled by the addition
# of "-DSIM_FIX" to the DEFINES. This should be enabled by default
# and eventually will not require the symbol definition
################################################################
DEFINES = -DFILESYS_STUB -DRDATA -DSIM_FIX
#####################################################################
#
# You might want to play with the CFLAGS, but if you use -O it may
# break the thread system. You might want to use -fno-inline if
# you need to call some inline functions from the debugger.
CFLAGS = -g -Wall -fwritable-strings $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED
LDFLAGS =
#####################################################################
CPP= cpp
CC = g++
LD = g++
AS = as
RM = rm
INCPATH = -I../network -I../filesys -I../userprog -I../threads -I../machine -I../lib
PROGRAM = nachos
#
# Edit these lists as if you add files to the source directories.
# See the instructions at the top of the file for more information.
#
LIB_H = ../lib/bitmap.h\
../lib/copyright.h\
../lib/debug.h\
../lib/hash.h\
../lib/libtest.h\
../lib/list.h\
../lib/sysdep.h\
../lib/utility.h
LIB_C = ../lib/bitmap.cc\
../lib/debug.cc\
../lib/hash.cc\
../lib/libtest.cc\
../lib/list.cc\
../lib/sysdep.cc
LIB_O = bitmap.o debug.o libtest.o sysdep.o
MACHINE_H = ../machine/callback.h\
../machine/interrupt.h\
../machine/stats.h\
../machine/timer.h\
../machine/console.h\
../machine/machine.h\
../machine/mipssim.h\
../machine/translate.h\
../machine/network.h\
../machine/disk.h
MACHINE_C = ../machine/interrupt.cc\
../machine/stats.cc\
../machine/timer.cc\
../machine/console.cc\
../machine/machine.cc\
../machine/mipssim.cc\
../machine/translate.cc\
../machine/network.cc\
../machine/disk.cc
MACHINE_O = interrupt.o stats.o timer.o console.o machine.o mipssim.o\
translate.o network.o disk.o
THREAD_H = ../threads/alarm.h\
../threads/kernel.h\
../threads/main.h\
../threads/scheduler.h\
../threads/switch.h\
../threads/synch.h\
../threads/synchlist.h\
../threads/thread.h
THREAD_C = ../threads/alarm.cc\
../threads/kernel.cc\
../threads/main.cc\
../threads/scheduler.cc\
../threads/synch.cc\
../threads/synchlist.cc\
../threads/thread.cc
THREAD_O = alarm.o kernel.o main.o scheduler.o synch.o thread.o
USERPROG_H = ../userprog/addrspace.h\
../userprog/syscall.h\
../userprog/synchconsole.h\
../userprog/noff.h
USERPROG_C = ../userprog/addrspace.cc\
../userprog/exception.cc\
../userprog/synchconsole.cc
USERPROG_O = addrspace.o exception.o synchconsole.o
FILESYS_H =../filesys/directory.h \
../filesys/filehdr.h\
../filesys/filesys.h \
../filesys/openfile.h\
../filesys/pbitmap.h\
../filesys/synchdisk.h
FILESYS_C =../filesys/directory.cc\
../filesys/filehdr.cc\
../filesys/filesys.cc\
../filesys/pbitmap.cc\
../filesys/openfile.cc\
../filesys/synchdisk.cc\
FILESYS_O =directory.o filehdr.o filesys.o pbitmap.o openfile.o synchdisk.o
NETWORK_H = ../network/post.h
NETWORK_C = ../network/post.cc
NETWORK_O = post.o
##################################################################
# You probably don't want to change anything below this point in
# the file unless you are comfortable with GNU make and know what
# you are doing...
##################################################################
THREAD_S = ../threads/switch.s
HFILES = $(LIB_H) $(MACHINE_H) $(THREAD_H) $(USERPROG_H) $(FILESYS_H) $(NETWORK_H)
CFILES = $(LIB_C) $(MACHINE_C) $(THREAD_C) $(USERPROG_C) $(FILESYS_C) $(NETWORK_C)
C_OFILES = $(LIB_O) $(MACHINE_O) $(THREAD_O) $(USERPROG_O) $(FILESYS_O) $(NETWORK_O)
S_OFILES = switch.o
OFILES = $(C_OFILES) $(S_OFILES)
$(PROGRAM): $(OFILES)
$(LD) $(OFILES) $(LDFLAGS) -o $(PROGRAM)
$(C_OFILES): %.o:
$(CC) $(CFLAGS) -c $<
switch.o: ../threads/switch.s
$(CPP) $(CPP_AS_FLAGS) -P $(INCPATH) $(HOSTCFLAGS) ../threads/switch.s > swtch.s
$(AS) -o switch.o swtch.s
depend: $(CFILES) $(HFILES)
$(CC) $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -M $(CFILES) > makedep
@echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
@echo '$$r makedep' >>eddep
@echo 'w' >>eddep
@echo 'q' >>eddep
ed - Makefile.dep < eddep
rm eddep makedep
@echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.dep
@echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.dep
@echo '# see make depend above' >> Makefile.dep
clean:
$(RM) -f $(OFILES)
$(RM) -f swtch.s
$(RM) -f *.s *.ii
distclean: clean
$(RM) -f $(PROGRAM)
$(RM) -f $(PROGRAM).exe
$(RM) -f DISK_?
$(RM) -f core
$(RM) -f SOCKET_?
include Makefile.dep

544
code/build.cygwin/Makefile.dep Executable file
View File

@@ -0,0 +1,544 @@
##################################################################
# Machine Dependencies - this file is included automatically
# into the main Makefile
#
# This file contains definitions below for x86 running Linux
# It has *not* been tested!
##################################################################
HOSTCFLAGS = -Dx86 -DLINUX -DCYGWIN
#-----------------------------------------------------------------
# Do not put anything below this point - it will be destroyed by
# "make depend"
#
# DO NOT DELETE THIS LINE -- make depend uses it
# DEPENDENCIES MUST END AT END OF FILE
bitmap.o: ../lib/bitmap.cc ../lib/copyright.h ../lib/debug.h \
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../lib/bitmap.h
debug.o: ../lib/debug.cc ../lib/copyright.h ../lib/utility.h \
../lib/debug.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h
hash.o: ../lib/hash.cc ../lib/copyright.h
libtest.o: ../lib/libtest.cc ../lib/copyright.h ../lib/libtest.h \
../lib/bitmap.h ../lib/utility.h ../lib/list.h ../lib/debug.h \
../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../lib/list.cc ../lib/hash.h ../lib/hash.cc
list.o: ../lib/list.cc ../lib/copyright.h
sysdep.o: ../lib/sysdep.cc ../lib/copyright.h ../lib/debug.h \
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h /usr/include/unistd.h /usr/include/sys/unistd.h \
/usr/include/getopt.h /usr/include/sys/time.h \
/usr/include/sys/select.h /usr/include/time.h \
/usr/include/machine/time.h /usr/include/sys/file.h \
/usr/include/fcntl.h /usr/include/sys/fcntl.h /usr/include/sys/stat.h \
/usr/include/cygwin/stat.h /usr/include/sys/socket.h \
/usr/include/features.h /usr/include/cygwin/socket.h \
/usr/include/asm/socket.h /usr/include/cygwin/if.h \
/usr/include/cygwin/sockios.h /usr/include/cygwin/uio.h \
/usr/include/sys/un.h /usr/include/signal.h /usr/include/sys/signal.h
interrupt.o: ../machine/interrupt.cc ../lib/copyright.h \
../machine/interrupt.h ../lib/list.h ../lib/debug.h ../lib/utility.h \
../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../lib/list.cc ../machine/callback.h \
../threads/main.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../machine/stats.h ../threads/alarm.h ../machine/timer.h
stats.o: ../machine/stats.cc ../lib/copyright.h ../lib/debug.h \
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../machine/stats.h
timer.o: ../machine/timer.cc ../lib/copyright.h ../machine/timer.h \
../lib/utility.h ../machine/callback.h ../threads/main.h \
../lib/debug.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
../machine/stats.h ../threads/alarm.h
console.o: ../machine/console.cc ../lib/copyright.h \
../machine/console.h ../lib/utility.h ../machine/callback.h \
../threads/main.h ../lib/debug.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
../machine/stats.h ../threads/alarm.h ../machine/timer.h
machine.o: ../machine/machine.cc ../lib/copyright.h \
../machine/machine.h ../lib/utility.h ../machine/translate.h \
../threads/main.h ../lib/debug.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../threads/scheduler.h ../lib/list.h ../lib/list.cc \
../machine/interrupt.h ../machine/callback.h ../machine/stats.h \
../threads/alarm.h ../machine/timer.h
mipssim.o: ../machine/mipssim.cc ../lib/copyright.h ../lib/debug.h \
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
../machine/mipssim.h ../threads/main.h ../threads/kernel.h \
../threads/thread.h ../userprog/addrspace.h ../filesys/filesys.h \
../filesys/openfile.h ../threads/scheduler.h ../lib/list.h \
../lib/list.cc ../machine/interrupt.h ../machine/callback.h \
../machine/stats.h ../threads/alarm.h ../machine/timer.h
translate.o: ../machine/translate.cc ../lib/copyright.h \
../threads/main.h ../lib/debug.h ../lib/utility.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
../machine/timer.h
network.o: ../machine/network.cc ../lib/copyright.h \
../machine/network.h ../lib/utility.h ../machine/callback.h \
../threads/main.h ../lib/debug.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
../machine/stats.h ../threads/alarm.h ../machine/timer.h
disk.o: ../machine/disk.cc ../lib/copyright.h ../machine/disk.h \
../lib/utility.h ../machine/callback.h ../lib/debug.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/main.h ../threads/kernel.h \
../threads/thread.h ../machine/machine.h ../machine/translate.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../threads/scheduler.h ../lib/list.h ../lib/list.cc \
../machine/interrupt.h ../machine/stats.h ../threads/alarm.h \
../machine/timer.h
alarm.o: ../threads/alarm.cc ../lib/copyright.h ../threads/alarm.h \
../lib/utility.h ../machine/callback.h ../machine/timer.h \
../threads/main.h ../lib/debug.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
../machine/stats.h
kernel.o: ../threads/kernel.cc ../lib/copyright.h ../lib/debug.h \
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/main.h ../threads/kernel.h \
../threads/thread.h ../machine/machine.h ../machine/translate.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../threads/scheduler.h ../lib/list.h ../lib/list.cc \
../machine/interrupt.h ../machine/callback.h ../machine/stats.h \
../threads/alarm.h ../machine/timer.h ../threads/synch.h \
../threads/synchlist.h ../threads/synchlist.cc ../lib/libtest.h \
../userprog/synchconsole.h ../machine/console.h \
../filesys/synchdisk.h ../machine/disk.h ../network/post.h \
../machine/network.h
main.o: ../threads/main.cc ../lib/copyright.h ../threads/main.h \
../lib/debug.h ../lib/utility.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
../machine/timer.h
scheduler.o: ../threads/scheduler.cc ../lib/copyright.h ../lib/debug.h \
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/scheduler.h ../lib/list.h \
../lib/list.cc ../threads/thread.h ../machine/machine.h \
../machine/translate.h ../userprog/addrspace.h ../filesys/filesys.h \
../filesys/openfile.h ../threads/main.h ../threads/kernel.h \
../machine/interrupt.h ../machine/callback.h ../machine/stats.h \
../threads/alarm.h ../machine/timer.h
synch.o: ../threads/synch.cc ../lib/copyright.h ../threads/synch.h \
../threads/thread.h ../lib/utility.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../lib/list.h ../lib/debug.h ../lib/list.cc ../threads/main.h \
../threads/kernel.h ../threads/scheduler.h ../machine/interrupt.h \
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
../machine/timer.h
synchlist.o: ../threads/synchlist.cc ../lib/copyright.h \
../threads/synchlist.h ../lib/list.h ../lib/debug.h ../lib/utility.h \
../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../lib/list.cc ../threads/synch.h \
../threads/thread.h ../machine/machine.h ../machine/translate.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../threads/main.h ../threads/kernel.h ../threads/scheduler.h \
../machine/interrupt.h ../machine/callback.h ../machine/stats.h \
../threads/alarm.h ../machine/timer.h ../threads/synchlist.cc
thread.o: ../threads/thread.cc ../lib/copyright.h ../threads/thread.h \
../lib/utility.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../threads/switch.h ../threads/synch.h ../lib/list.h ../lib/debug.h \
../lib/list.cc ../threads/main.h ../threads/kernel.h \
../threads/scheduler.h ../machine/interrupt.h ../machine/callback.h \
../machine/stats.h ../threads/alarm.h ../machine/timer.h
addrspace.o: ../userprog/addrspace.cc ../lib/copyright.h \
../threads/main.h ../lib/debug.h ../lib/utility.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
../machine/timer.h ../userprog/noff.h
exception.o: ../userprog/exception.cc ../lib/copyright.h \
../threads/main.h ../lib/debug.h ../lib/utility.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../threads/kernel.h ../threads/thread.h \
../machine/machine.h ../machine/translate.h ../userprog/addrspace.h \
../filesys/filesys.h ../filesys/openfile.h ../threads/scheduler.h \
../lib/list.h ../lib/list.cc ../machine/interrupt.h \
../machine/callback.h ../machine/stats.h ../threads/alarm.h \
../machine/timer.h ../userprog/syscall.h ../userprog/errno.h \
../userprog/ksyscall.h
synchconsole.o: ../userprog/synchconsole.cc ../lib/copyright.h \
../userprog/synchconsole.h ../lib/utility.h ../machine/callback.h \
../machine/console.h ../threads/synch.h ../threads/thread.h \
../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../lib/list.h ../lib/debug.h ../lib/list.cc ../threads/main.h \
../threads/kernel.h ../threads/scheduler.h ../machine/interrupt.h \
../machine/stats.h ../threads/alarm.h ../machine/timer.h
directory.o: ../filesys/directory.cc ../lib/copyright.h \
../lib/utility.h ../filesys/filehdr.h ../machine/disk.h \
../machine/callback.h ../filesys/pbitmap.h ../lib/bitmap.h \
../filesys/openfile.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../filesys/directory.h
filehdr.o: ../filesys/filehdr.cc ../lib/copyright.h \
../filesys/filehdr.h ../machine/disk.h ../lib/utility.h \
../machine/callback.h ../filesys/pbitmap.h ../lib/bitmap.h \
../filesys/openfile.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../lib/debug.h ../filesys/synchdisk.h \
../threads/synch.h ../threads/thread.h ../machine/machine.h \
../machine/translate.h ../userprog/addrspace.h ../filesys/filesys.h \
../lib/list.h ../lib/list.cc ../threads/main.h ../threads/kernel.h \
../threads/scheduler.h ../machine/interrupt.h ../machine/stats.h \
../threads/alarm.h ../machine/timer.h
filesys.o: ../filesys/filesys.cc
pbitmap.o: ../filesys/pbitmap.cc ../lib/copyright.h \
../filesys/pbitmap.h ../lib/bitmap.h ../lib/utility.h \
../filesys/openfile.h ../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h
openfile.o: ../filesys/openfile.cc
synchdisk.o: ../filesys/synchdisk.cc ../lib/copyright.h \
../filesys/synchdisk.h ../machine/disk.h ../lib/utility.h \
../machine/callback.h ../threads/synch.h ../threads/thread.h \
../lib/sysdep.h /usr/include/g++-3/iostream.h \
/usr/include/g++-3/streambuf.h /usr/include/g++-3/libio.h \
/usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../machine/machine.h ../machine/translate.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../lib/list.h ../lib/debug.h ../lib/list.cc ../threads/main.h \
../threads/kernel.h ../threads/scheduler.h ../machine/interrupt.h \
../machine/stats.h ../threads/alarm.h ../machine/timer.h
post.o: ../network/post.cc ../lib/copyright.h ../network/post.h \
../lib/utility.h ../machine/callback.h ../machine/network.h \
../threads/synchlist.h ../lib/list.h ../lib/debug.h ../lib/sysdep.h \
/usr/include/g++-3/iostream.h /usr/include/g++-3/streambuf.h \
/usr/include/g++-3/libio.h /usr/include/_G_config.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stddef.h \
/usr/include/sys/cdefs.h /usr/include/stdlib.h /usr/include/_ansi.h \
/usr/include/sys/config.h /usr/include/sys/reent.h \
/usr/include/sys/_types.h /usr/include/machine/stdlib.h \
/usr/include/alloca.h /usr/include/stdio.h \
/usr/lib/gcc-lib/i686-pc-cygwin/2.95.3-5/include/stdarg.h \
/usr/include/sys/types.h /usr/include/machine/types.h \
/usr/include/sys/features.h /usr/include/cygwin/types.h \
/usr/include/sys/sysmacros.h /usr/include/sys/stdio.h \
/usr/include/string.h ../lib/list.cc ../threads/synch.h \
../threads/thread.h ../machine/machine.h ../machine/translate.h \
../userprog/addrspace.h ../filesys/filesys.h ../filesys/openfile.h \
../threads/main.h ../threads/kernel.h ../threads/scheduler.h \
../machine/interrupt.h ../machine/stats.h ../threads/alarm.h \
../machine/timer.h ../threads/synchlist.cc
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above

BIN
code/build.linux/DISK_0 Executable file

Binary file not shown.

372
code/build.linux/Makefile Executable file
View File

@@ -0,0 +1,372 @@
# Copyright (c) 1992-1996 The Regents of the University of California.
# All rights reserved. See copyright.h for copyright notice and limitation
# of liability and disclaimer of warranty provisions.
#
# This is a GNU Makefile. It must be used with the GNU make program.
# At UW, the GNU make program is /software/gnu/bin/make.
# In many other places it is known as "gmake".
# You may wish to include /software/gnu/bin/ early in your command
# search path, so that you will be using GNU make when you type "make".
#
# About this Makefile:
# --------------------
#
# This Makefile is used to build the Nachos system, which includes
# the MIPS machine simulation and a simple operating system.
#
# There is a separate Makefile, in the "test" directory, that is
# used to build the Nachos test programs (which run on the
# simulated machine).
#
# There are several "build" directories, one for each type
# of machine in the MFCF computing environment
# (build.solaris, build.sunos, and build.ultrix), as well
# as a build directory for Linux (build.linux) and a generic
# build directory (build.other) for those who wish to try
# building Nachos on other platforms.
#
# This Makefile appears to be located in all of the build directories.
# If you edit it in one directory, the copies in all of the other
# directories appear to change as well. This is the desired behaviour,
# since this file is machine independent. (The file actually lives
# in build.solaris, with symbolic links from the other build directories.)
#
# The platform-dependent parts of make's instructions are located
# in the file Makefile.dep.
# There is a different Makefile.dep in each build directory.
#
# If you are in the MFCF environment, you should not have to edit
# the Makefile.dep files by hand. Any changes to the make instructions
# can be made in this file (see the instructions below) - they will
# apply no matter where you build Nachos.
# If you are not in the MFCF environment, e.g., if you are trying
# to build Nachos on Linux at home, you will probably need
# to edit Makefile.dep (in the appropriate build directory) to
# customize the make procedure to your environment.
#
# How to build Nachos for the first time:
# ---------------------------------------
#
# (1) Make sure than you are in the build directory for the
# type of machine you are logged in to (the "host" machine):
#
# host type examples build directory
# ----------- ----------- ----------------
#
# sparc/SunOS cayley,napier, build.sunos
# (SunOS 4.1.3) descartes
#
# sparc/Solaris picard.math, build.solaris
# (SunOS 5.x) hermite.math,
# markov.math,
# hypatia.math,
# hume.math
#
# mips/ULTRIX cantor.math build.ultrix
# (ULTRIX 4.2) noether.math
#
# If you are not sure what type of machine you are on,
# try the command "uname -a".
#
# (2) Type "make depend"
# - this computes file dependencies and records them
# at the end of the file Makefile.dep in
# your build directory. Have a look...
#
# (3) Type "make nachos" (or just "make").
# - make echos the commands it is executing, so that
# you can observe its progress. When the
# build is finished, you should have an
# executable "nachos" in the build directory.
#
# (4) There is no 4th step. You are done. Try running "./nachos -u".
#
#
# How to Re-build Nachos after you have changed the code:
#--------------------------------------------------------
#
# - The Nachos source code is located in the code subdirectories:
# threads, userprog, filesys, network, and lib. You may
# change the files in any of these directories, and you can
# add new files and/or remove files. The "machine" subdirectory
# contains the hardware simulation (which is also part of
# Nachos. You may look at it, but
# you may not change it, except as noted in machine/machine.h
# - When you want to re-make Nachos, always do it in the
# "build" directory that is appropriate for the machine
# type that you are running on.
# DO NOT TRY TO MAKE NACHOS IN THE SOURCE CODE DIRECTORIES.
#
# - IF all you have done is changed C++ code in existing files
# (since the last time you made Nachos in this build directory),
# THEN all you need to do to re-make Nachos is to type
#
# "make nachos"
#
# in the build directory.
#
# - IF you have done any of the following since the last build in
# this directory:
# added new .cc files or new .h files
# added or deleted #include's from existing files
# THEN
# you must do
# "make depend"
# followed by
# "make nachos"
#
# in the build directory.
#
# Note that is is always safe to do "make depend" followed by
# "make nachos", so if you are not sure what changes you have
# made, do "make depend".
#
# - IF you have added new files (.cc or .h) since the last build,
# you should edit this Makefile before running "make depend"
# and "make nachos".
# For new .h files, simply update the appropriate "_H" list below.
# For example, if you create a file called
# "bigfile.h" in the filesys subdirectory, you should add
# "../filesys/bigfile.h" to FILESYS_H, which is defined below
# For new .cc files, update the appropriate "_C" and "_O" lists.
# For example, if you create a file called "filetable.cc" in
# the directory "userprog", you should add
# "../userprog/filetable.cc" to USERPROG_C,
# and you should add "filetable.o" to USERPROG_O.
# Note that the entry in the "_C" list includes the subdirectory
# name, while the entry on the "_O" list does not.
#
# Some Important Notes:
# ---------------------
#
# * You can clean up all of the .o and other files left behind
# by make by typeing "make clean" in the build directory.
# * You can clean up .o and other files, as well as the nachos
# executable, DISK, core, SOCKET, and other files by typing
# make "distclean"
#
# These are good ways to save space, but the next build that
# you do after cleaning will take longer than usual, since
# much of the stuff you cleaned will need to be rebuilt.
#
# * When you build Nachos on an ULTRIX machine (in build.ultrix),
# you will get lots of warning messages like this:
#
# openfile.o: does not have gp tables for all it's sectons
#
# from the loader. Ignore them. Or better yet, figure out
# how to make them go away.
#
# The Most Important Note:
# -----------------------
#
# * If "make" is behaving strangely and you cannot figure out
# why, you should REBUILD the program FROM SCRATCH.
# Yes, it is slow.
# But, there are lots of little things that can go wrong, especially
# with all of these different types of machines available.
# Rebuilding from scratch at least gives you a known starting
# place. To rebuild from scratch, go to the appropriate
# build directory and do:
#
# make distclean
# make depend
# make nachos
#
################################################################
# READ THIS: CONFIGURING NACHOS
#
# Change DEFINES (below) to
# DEFINES = -DUSE_TLB -DFILESYS_STUB
# if you want the simulated machine to use its TLB
#
# If you want to use the real Nachos file system (based on
# the simulated disk), rather than the stub, remove
# the -DFILESYS_STUB from DEFINES.
#
# There is a a fix to the MIPS simulator to enable it to properly
# handle unaligned data access. This fix is enabled by the addition
# of "-DSIM_FIX" to the DEFINES. This should be enabled by default
# and eventually will not require the symbol definition
################################################################
DEFINES = -DFILESYS_STUB -DRDATA -DSIM_FIX
#####################################################################
#
# You might want to play with the CFLAGS, but if you use -O it may
# break the thread system. You might want to use -fno-inline if
# you need to call some inline functions from the debugger.
CFLAGS = -g -Wall $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -m32
LDFLAGS = -m32
CPP_AS_FLAGS= -m32
#####################################################################
CPP=/lib/cpp
CC = g++ -m32 -Wno-deprecated
LD = g++ -m32 -Wno-deprecated
AS = as --32
RM = /bin/rm
INCPATH = -I../network -I../filesys -I../userprog -I../threads -I../machine -I../lib -I-
PROGRAM = nachos
#
# Edit these lists as if you add files to the source directories.
# See the instructions at the top of the file for more information.
#
LIB_H = ../lib/bitmap.h\
../lib/copyright.h\
../lib/debug.h\
../lib/hash.h\
../lib/libtest.h\
../lib/list.h\
../lib/sysdep.h\
../lib/utility.h
LIB_C = ../lib/bitmap.cc\
../lib/debug.cc\
../lib/hash.cc\
../lib/libtest.cc\
../lib/list.cc\
../lib/sysdep.cc
LIB_O = bitmap.o debug.o libtest.o sysdep.o
MACHINE_H = ../machine/callback.h\
../machine/interrupt.h\
../machine/stats.h\
../machine/timer.h\
../machine/console.h\
../machine/machine.h\
../machine/mipssim.h\
../machine/translate.h\
../machine/network.h\
../machine/disk.h
MACHINE_C = ../machine/interrupt.cc\
../machine/stats.cc\
../machine/timer.cc\
../machine/console.cc\
../machine/machine.cc\
../machine/mipssim.cc\
../machine/translate.cc\
../machine/network.cc\
../machine/disk.cc
MACHINE_O = interrupt.o stats.o timer.o console.o machine.o mipssim.o\
translate.o network.o disk.o
THREAD_H = ../threads/alarm.h\
../threads/kernel.h\
../threads/main.h\
../threads/scheduler.h\
../threads/switch.h\
../threads/synch.h\
../threads/synchlist.h\
../threads/thread.h
THREAD_C = ../threads/alarm.cc\
../threads/kernel.cc\
../threads/main.cc\
../threads/scheduler.cc\
../threads/synch.cc\
../threads/synchlist.cc\
../threads/thread.cc
THREAD_O = alarm.o kernel.o main.o scheduler.o synch.o thread.o
USERPROG_H = ../userprog/addrspace.h\
../userprog/syscall.h\
../userprog/synchconsole.h\
../userprog/noff.h
USERPROG_C = ../userprog/addrspace.cc\
../userprog/exception.cc\
../userprog/synchconsole.cc
USERPROG_O = addrspace.o exception.o synchconsole.o
FILESYS_H =../filesys/directory.h \
../filesys/filehdr.h\
../filesys/filesys.h \
../filesys/openfile.h\
../filesys/pbitmap.h\
../filesys/synchdisk.h
FILESYS_C =../filesys/directory.cc\
../filesys/filehdr.cc\
../filesys/filesys.cc\
../filesys/pbitmap.cc\
../filesys/openfile.cc\
../filesys/synchdisk.cc\
FILESYS_O =directory.o filehdr.o filesys.o pbitmap.o openfile.o synchdisk.o
NETWORK_H = ../network/post.h
NETWORK_C = ../network/post.cc
NETWORK_O = post.o
##################################################################
# You probably don't want to change anything below this point in
# the file unless you are comfortable with GNU make and know what
# you are doing...
##################################################################
THREAD_S = ../threads/switch.s
HFILES = $(LIB_H) $(MACHINE_H) $(THREAD_H) $(USERPROG_H) $(FILESYS_H) $(NETWORK_H)
CFILES = $(LIB_C) $(MACHINE_C) $(THREAD_C) $(USERPROG_C) $(FILESYS_C) $(NETWORK_C)
C_OFILES = $(LIB_O) $(MACHINE_O) $(THREAD_O) $(USERPROG_O) $(FILESYS_O) $(NETWORK_O)
S_OFILES = switch.o
OFILES = $(C_OFILES) $(S_OFILES)
$(PROGRAM): $(OFILES)
$(LD) $(OFILES) $(LDFLAGS) -o $(PROGRAM)
$(C_OFILES): %.o:
$(CC) $(CFLAGS) -c $<
switch.o: ../threads/switch.S
$(CC) $(CPP_AS_FLAGS) -P $(INCPATH) $(HOSTCFLAGS) -c ../threads/switch.S
depend: $(CFILES) $(HFILES)
$(CC) $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -M $(CFILES) > makedep
@echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep
@echo '$$r makedep' >>eddep
@echo 'w' >>eddep
@echo 'q' >>eddep
ed - Makefile.dep < eddep
rm eddep makedep
@echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.dep
@echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.dep
@echo '# see make depend above' >> Makefile.dep
clean:
$(RM) -f $(OFILES)
distclean: clean
$(RM) -f $(PROGRAM)
$(RM) -f DISK_?
$(RM) -f core
$(RM) -f SOCKET_?
@echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep
@echo 'w' >>eddep
@echo 'q' >>eddep
ed - Makefile.dep < eddep
rm eddep
@echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.dep
@echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.dep
@echo '# see make depend above' >> Makefile.dep
include Makefile.dep

3703
code/build.linux/Makefile.dep Executable file

File diff suppressed because it is too large Load Diff

BIN
code/build.linux/nachos Executable file

Binary file not shown.

365
code/build.macosx/Makefile Executable file
View File

@@ -0,0 +1,365 @@
# Copyright (c) 1992-1996 The Regents of the University of California.
# All rights reserved. See copyright.h for copyright notice and limitation
# of liability and disclaimer of warranty provisions.
#
# This is a GNU Makefile. It must be used with the GNU make program.
# At UW, the GNU make program is /software/gnu/bin/make.
# In many other places it is known as "gmake".
# You may wish to include /software/gnu/bin/ early in your command
# search path, so that you will be using GNU make when you type "make".
#
# About this Makefile:
# --------------------
#
# This Makefile is used to build the Nachos system, which includes
# the MIPS machine simulation and a simple operating system.
#
# There is a separate Makefile, in the "test" directory, that is
# used to build the Nachos test programs (which run on the
# simulated machine).
#
# There are several "build" directories, one for each type
# of machine in the MFCF computing environment
# (build.solaris, build.sunos, and build.ultrix), as well
# as a build directory for Linux (build.linux) and a generic
# build directory (build.other) for those who wish to try
# building Nachos on other platforms.
#
# This Makefile appears to be located in all of the build directories.
# If you edit it in one directory, the copies in all of the other
# directories appear to change as well. This is the desired behaviour,
# since this file is machine independent. (The file actually lives
# in build.solaris, with symbolic links from the other build directories.)
#
# The platform-dependent parts of make's instructions are located
# in the file Makefile.dep.
# There is a different Makefile.dep in each build directory.
#
# If you are in the MFCF environment, you should not have to edit
# the Makefile.dep files by hand. Any changes to the make instructions
# can be made in this file (see the instructions below) - they will
# apply no matter where you build Nachos.
# If you are not in the MFCF environment, e.g., if you are trying
# to build Nachos on Linux at home, you will probably need
# to edit Makefile.dep (in the appropriate build directory) to
# customize the make procedure to your environment.
#
# How to build Nachos for the first time:
# ---------------------------------------
#
# (1) Make sure than you are in the build directory for the
# type of machine you are logged in to (the "host" machine):
#
# host type examples build directory
# ----------- ----------- ----------------
#
# sparc/SunOS cayley,napier, build.sunos
# (SunOS 4.1.3) descartes
#
# sparc/Solaris picard.math, build.solaris
# (SunOS 5.x) hermite.math,
# markov.math,
# hypatia.math,
# hume.math
#
# mips/ULTRIX cantor.math build.ultrix
# (ULTRIX 4.2) noether.math
#
# If you are not sure what type of machine you are on,
# try the command "uname -a".
#
# (2) Type "make depend"
# - this computes file dependencies and records them
# at the end of the file Makefile.dep in
# your build directory. Have a look...
#
# (3) Type "make nachos" (or just "make").
# - make echos the commands it is executing, so that
# you can observe its progress. When the
# build is finished, you should have an
# executable "nachos" in the build directory.
#
# (4) There is no 4th step. You are done. Try running "./nachos -u".
#
#
# How to Re-build Nachos after you have changed the code:
#--------------------------------------------------------
#
# - The Nachos source code is located in the code subdirectories:
# threads, userprog, filesys, network, and lib. You may
# change the files in any of these directories, and you can
# add new files and/or remove files. The "machine" subdirectory
# contains the hardware simulation (which is also part of
# Nachos. You may look at it, but
# you may not change it, except as noted in machine/machine.h
# - When you want to re-make Nachos, always do it in the
# "build" directory that is appropriate for the machine
# type that you are running on.
# DO NOT TRY TO MAKE NACHOS IN THE SOURCE CODE DIRECTORIES.
#
# - IF all you have done is changed C++ code in existing files
# (since the last time you made Nachos in this build directory),
# THEN all you need to do to re-make Nachos is to type
#
# "make nachos"
#
# in the build directory.
#
# - IF you have done any of the following since the last build in
# this directory:
# added new .cc files or new .h files
# added or deleted #include's from existing files
# THEN
# you must do
# "make depend"
# followed by
# "make nachos"
#
# in the build directory.
#
# Note that is is always safe to do "make depend" followed by
# "make nachos", so if you are not sure what changes you have
# made, do "make depend".
#
# - IF you have added new files (.cc or .h) since the last build,
# you should edit this Makefile before running "make depend"
# and "make nachos".
# For new .h files, simply update the appropriate "_H" list below.
# For example, if you create a file called
# "bigfile.h" in the filesys subdirectory, you should add
# "../filesys/bigfile.h" to FILESYS_H, which is defined below
# For new .cc files, update the appropriate "_C" and "_O" lists.
# For example, if you create a file called "filetable.cc" in
# the directory "userprog", you should add
# "../userprog/filetable.cc" to USERPROG_C,
# and you should add "filetable.o" to USERPROG_O.
# Note that the entry in the "_C" list includes the subdirectory
# name, while the entry on the "_O" list does not.
#
# Some Important Notes:
# ---------------------
#
# * You can clean up all of the .o and other files left behind
# by make by typeing "make clean" in the build directory.
# * You can clean up .o and other files, as well as the nachos
# executable, DISK, core, SOCKET, and other files by typing
# make "distclean"
#
# These are good ways to save space, but the next build that
# you do after cleaning will take longer than usual, since
# much of the stuff you cleaned will need to be rebuilt.
#
# * When you build Nachos on an ULTRIX machine (in build.ultrix),
# you will get lots of warning messages like this:
#
# openfile.o: does not have gp tables for all it's sectons
#
# from the loader. Ignore them. Or better yet, figure out
# how to make them go away.
#
# The Most Important Note:
# -----------------------
#
# * If "make" is behaving strangely and you cannot figure out
# why, you should REBUILD the program FROM SCRATCH.
# Yes, it is slow.
# But, there are lots of little things that can go wrong, especially
# with all of these different types of machines available.
# Rebuilding from scratch at least gives you a known starting
# place. To rebuild from scratch, go to the appropriate
# build directory and do:
#
# make distclean
# make depend
# make nachos
#
################################################################
# READ THIS: CONFIGURING NACHOS
#
# Change DEFINES (below) to
# DEFINES = -DUSE_TLB -DFILESYS_STUB
# if you want the simulated machine to use its TLB
#
# If you want to use the real Nachos file system (based on
# the simulated disk), rather than the stub, remove
# the -DFILESYS_STUB from DEFINES.
#
# There is a a fix to the MIPS simulator to enable it to properly
# handle unaligned data access. This fix is enabled by the addition
# of "-DSIM_FIX" to the DEFINES. This should be enabled by default
# and eventually will not require the symbol definition
################################################################
DEFINES = -DFILESYS_STUB -DRDATA -DSIM_FIX
#####################################################################
#
# You might want to play with the CFLAGS, but if you use -O it may
# break the thread system. You might want to use -fno-inline if
# you need to call some inline functions from the debugger.
CFLAGS = -g -Wall -fwritable-strings $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED
LDFLAGS =
#####################################################################
CPP=/lib/cpp
CC = g++
LD = g++
AS = as
RM = /bin/rm
INCPATH = -I../network -I../filesys -I../userprog -I../threads -I../machine -I../lib
PROGRAM = nachos
#
# Edit these lists as if you add files to the source directories.
# See the instructions at the top of the file for more information.
#
LIB_H = ../lib/bitmap.h\
../lib/copyright.h\
../lib/debug.h\
../lib/hash.h\
../lib/libtest.h\
../lib/list.h\
../lib/sysdep.h\
../lib/utility.h
LIB_C = ../lib/bitmap.cc\
../lib/debug.cc\
../lib/hash.cc\
../lib/libtest.cc\
../lib/list.cc\
../lib/sysdep.cc
LIB_O = bitmap.o debug.o libtest.o sysdep.o
MACHINE_H = ../machine/callback.h\
../machine/interrupt.h\
../machine/stats.h\
../machine/timer.h\
../machine/console.h\
../machine/machine.h\
../machine/mipssim.h\
../machine/translate.h\
../machine/network.h\
../machine/disk.h
MACHINE_C = ../machine/interrupt.cc\
../machine/stats.cc\
../machine/timer.cc\
../machine/console.cc\
../machine/machine.cc\
../machine/mipssim.cc\
../machine/translate.cc\
../machine/network.cc\
../machine/disk.cc
MACHINE_O = interrupt.o stats.o timer.o console.o machine.o mipssim.o\
translate.o network.o disk.o
THREAD_H = ../threads/alarm.h\
../threads/kernel.h\
../threads/main.h\
../threads/scheduler.h\
../threads/switch.h\
../threads/synch.h\
../threads/synchlist.h\
../threads/thread.h
THREAD_C = ../threads/alarm.cc\
../threads/kernel.cc\
../threads/main.cc\
../threads/scheduler.cc\
../threads/synch.cc\
../threads/synchlist.cc\
../threads/thread.cc
THREAD_O = alarm.o kernel.o main.o scheduler.o synch.o thread.o
USERPROG_H = ../userprog/addrspace.h\
../userprog/syscall.h\
../userprog/synchconsole.h\
../userprog/noff.h
USERPROG_C = ../userprog/addrspace.cc\
../userprog/exception.cc\
../userprog/synchconsole.cc
USERPROG_O = addrspace.o exception.o synchconsole.o
FILESYS_H =../filesys/directory.h \
../filesys/filehdr.h\
../filesys/filesys.h \
../filesys/openfile.h\
../filesys/pbitmap.h\
../filesys/synchdisk.h
FILESYS_C =../filesys/directory.cc\
../filesys/filehdr.cc\
../filesys/filesys.cc\
../filesys/pbitmap.cc\
../filesys/openfile.cc\
../filesys/synchdisk.cc\
FILESYS_O =directory.o filehdr.o filesys.o pbitmap.o openfile.o synchdisk.o
NETWORK_H = ../network/post.h
NETWORK_C = ../network/post.cc
NETWORK_O = post.o
##################################################################
# You probably don't want to change anything below this point in
# the file unless you are comfortable with GNU make and know what
# you are doing...
##################################################################
THREAD_S = ../threads/switch.s
HFILES = $(LIB_H) $(MACHINE_H) $(THREAD_H) $(USERPROG_H) $(FILESYS_H) $(NETWORK_H)
CFILES = $(LIB_C) $(MACHINE_C) $(THREAD_C) $(USERPROG_C) $(FILESYS_C) $(NETWORK_C)
C_OFILES = $(LIB_O) $(MACHINE_O) $(THREAD_O) $(USERPROG_O) $(FILESYS_O) $(NETWORK_O)
S_OFILES = switch.o
OFILES = $(C_OFILES) $(S_OFILES)
$(PROGRAM): $(OFILES)
$(LD) $(OFILES) $(LDFLAGS) -o $(PROGRAM)
$(C_OFILES): %.o:
$(CC) $(CFLAGS) -c $<
switch.o: ../threads/switch.s
$(CPP) $(CPP_AS_FLAGS) -P $(INCPATH) $(HOSTCFLAGS) ../threads/switch.s > swtch.s
$(AS) -o switch.o swtch.s
depend: $(CFILES) $(HFILES)
$(CC) $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -M $(CFILES) > makedep
@echo '/^# DO NOT DELETE THIS LINE/+2,$$d' >eddep
@echo '$$r makedep' >>eddep
@echo 'w' >>eddep
@echo 'q' >>eddep
ed - Makefile.dep < eddep
rm eddep makedep
@echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile.dep
@echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile.dep
@echo '# see make depend above' >> Makefile.dep
clean:
$(RM) -f $(OFILES)
$(RM) -f swtch.s
distclean: clean
$(RM) -f $(PROGRAM)
$(RM) -f DISK_?
$(RM) -f core
$(RM) -f SOCKET_?
include Makefile.dep

18
code/build.macosx/Makefile.dep Executable file
View File

@@ -0,0 +1,18 @@
##################################################################
# Machine Dependencies - this file is included automatically
# into the main Makefile
#
##################################################################
HOSTCFLAGS = -DHOST_IS_BIG_ENDIAN -DPowerPC -DBSD -DAIX -DApplePowerPC
CPP_AS_FLAGS = -D_ASM
CPP = cpp
#-----------------------------------------------------------------
# Do not put anything below this point - it will be destroyed by
# "make depend"
#
# DO NOT DELETE THIS LINE -- make depend uses it
# DEPENDENCIES MUST END AT END OF FILE
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see make depend above

196
code/filesys/directory.cc Executable file
View File

@@ -0,0 +1,196 @@
// directory.cc
// Routines to manage a directory of file names.
//
// The directory is a table of fixed length entries; each
// entry represents a single file, and contains the file name,
// and the location of the file header on disk. The fixed size
// of each directory entry means that we have the restriction
// of a fixed maximum size for file names.
//
// The constructor initializes an empty directory of a certain size;
// we use ReadFrom/WriteBack to fetch the contents of the directory
// from disk, and to write back any modifications back to disk.
//
// Also, this implementation has the restriction that the size
// of the directory cannot expand. In other words, once all the
// entries in the directory are used, no more files can be created.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "utility.h"
#include "filehdr.h"
#include "directory.h"
//----------------------------------------------------------------------
// Directory::Directory
// Initialize a directory; initially, the directory is completely
// empty. If the disk is being formatted, an empty directory
// is all we need, but otherwise, we need to call FetchFrom in order
// to initialize it from disk.
//
// "size" is the number of entries in the directory
//----------------------------------------------------------------------
Directory::Directory(int size)
{
table = new DirectoryEntry[size];
tableSize = size;
for (int i = 0; i < tableSize; i++)
table[i].inUse = FALSE;
}
//----------------------------------------------------------------------
// Directory::~Directory
// De-allocate directory data structure.
//----------------------------------------------------------------------
Directory::~Directory()
{
delete [] table;
}
//----------------------------------------------------------------------
// Directory::FetchFrom
// Read the contents of the directory from disk.
//
// "file" -- file containing the directory contents
//----------------------------------------------------------------------
void
Directory::FetchFrom(OpenFile *file)
{
(void) file->ReadAt((char *)table, tableSize * sizeof(DirectoryEntry), 0);
}
//----------------------------------------------------------------------
// Directory::WriteBack
// Write any modifications to the directory back to disk
//
// "file" -- file to contain the new directory contents
//----------------------------------------------------------------------
void
Directory::WriteBack(OpenFile *file)
{
(void) file->WriteAt((char *)table, tableSize * sizeof(DirectoryEntry), 0);
}
//----------------------------------------------------------------------
// Directory::FindIndex
// Look up file name in directory, and return its location in the table of
// directory entries. Return -1 if the name isn't in the directory.
//
// "name" -- the file name to look up
//----------------------------------------------------------------------
int
Directory::FindIndex(char *name)
{
for (int i = 0; i < tableSize; i++)
if (table[i].inUse && !strncmp(table[i].name, name, FileNameMaxLen))
return i;
return -1; // name not in directory
}
//----------------------------------------------------------------------
// Directory::Find
// Look up file name in directory, and return the disk sector number
// where the file's header is stored. Return -1 if the name isn't
// in the directory.
//
// "name" -- the file name to look up
//----------------------------------------------------------------------
int
Directory::Find(char *name)
{
int i = FindIndex(name);
if (i != -1)
return table[i].sector;
return -1;
}
//----------------------------------------------------------------------
// Directory::Add
// Add a file into the directory. Return TRUE if successful;
// return FALSE if the file name is already in the directory, or if
// the directory is completely full, and has no more space for
// additional file names.
//
// "name" -- the name of the file being added
// "newSector" -- the disk sector containing the added file's header
//----------------------------------------------------------------------
bool
Directory::Add(char *name, int newSector)
{
if (FindIndex(name) != -1)
return FALSE;
for (int i = 0; i < tableSize; i++)
if (!table[i].inUse) {
table[i].inUse = TRUE;
strncpy(table[i].name, name, FileNameMaxLen);
table[i].sector = newSector;
return TRUE;
}
return FALSE; // no space. Fix when we have extensible files.
}
//----------------------------------------------------------------------
// Directory::Remove
// Remove a file name from the directory. Return TRUE if successful;
// return FALSE if the file isn't in the directory.
//
// "name" -- the file name to be removed
//----------------------------------------------------------------------
bool
Directory::Remove(char *name)
{
int i = FindIndex(name);
if (i == -1)
return FALSE; // name not in directory
table[i].inUse = FALSE;
return TRUE;
}
//----------------------------------------------------------------------
// Directory::List
// List all the file names in the directory.
//----------------------------------------------------------------------
void
Directory::List()
{
for (int i = 0; i < tableSize; i++)
if (table[i].inUse)
printf("%s\n", table[i].name);
}
//----------------------------------------------------------------------
// Directory::Print
// List all the file names in the directory, their FileHeader locations,
// and the contents of each file. For debugging.
//----------------------------------------------------------------------
void
Directory::Print()
{
FileHeader *hdr = new FileHeader;
printf("Directory contents:\n");
for (int i = 0; i < tableSize; i++)
if (table[i].inUse) {
printf("Name: %s, Sector: %d\n", table[i].name, table[i].sector);
hdr->FetchFrom(table[i].sector);
hdr->Print();
}
printf("\n");
delete hdr;
}

83
code/filesys/directory.h Executable file
View File

@@ -0,0 +1,83 @@
// directory.h
// Data structures to manage a UNIX-like directory of file names.
//
// A directory is a table of pairs: <file name, sector #>,
// giving the name of each file in the directory, and
// where to find its file header (the data structure describing
// where to find the file's data blocks) on disk.
//
// We assume mutual exclusion is provided by the caller.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#ifndef DIRECTORY_H
#define DIRECTORY_H
#include "openfile.h"
#define FileNameMaxLen 9 // for simplicity, we assume
// file names are <= 9 characters long
// The following class defines a "directory entry", representing a file
// in the directory. Each entry gives the name of the file, and where
// the file's header is to be found on disk.
//
// Internal data structures kept public so that Directory operations can
// access them directly.
class DirectoryEntry {
public:
bool inUse; // Is this directory entry in use?
int sector; // Location on disk to find the
// FileHeader for this file
char name[FileNameMaxLen + 1]; // Text name for file, with +1 for
// the trailing '\0'
};
// The following class defines a UNIX-like "directory". Each entry in
// the directory describes a file, and where to find it on disk.
//
// The directory data structure can be stored in memory, or on disk.
// When it is on disk, it is stored as a regular Nachos file.
//
// The constructor initializes a directory structure in memory; the
// FetchFrom/WriteBack operations shuffle the directory information
// from/to disk.
class Directory {
public:
Directory(int size); // Initialize an empty directory
// with space for "size" files
~Directory(); // De-allocate the directory
void FetchFrom(OpenFile *file); // Init directory contents from disk
void WriteBack(OpenFile *file); // Write modifications to
// directory contents back to disk
int Find(char *name); // Find the sector number of the
// FileHeader for file: "name"
bool Add(char *name, int newSector); // Add a file name into the directory
bool Remove(char *name); // Remove a file from the directory
void List(); // Print the names of all the files
// in the directory
void Print(); // Verbose print of the contents
// of the directory -- all the file
// names and their contents.
private:
int tableSize; // Number of directory entries
DirectoryEntry *table; // Table of pairs:
// <file name, file header location>
int FindIndex(char *name); // Find the index into the directory
// table corresponding to "name"
};
#endif // DIRECTORY_H

156
code/filesys/filehdr.cc Executable file
View File

@@ -0,0 +1,156 @@
// filehdr.cc
// Routines for managing the disk file header (in UNIX, this
// would be called the i-node).
//
// The file header is used to locate where on disk the
// file's data is stored. We implement this as a fixed size
// table of pointers -- each entry in the table points to the
// disk sector containing that portion of the file data
// (in other words, there are no indirect or doubly indirect
// blocks). The table size is chosen so that the file header
// will be just big enough to fit in one disk sector,
//
// Unlike in a real system, we do not keep track of file permissions,
// ownership, last modification date, etc., in the file header.
//
// A file header can be initialized in two ways:
// for a new file, by modifying the in-memory data structure
// to point to the newly allocated data blocks
// for a file already on disk, by reading the file header from disk
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "filehdr.h"
#include "debug.h"
#include "synchdisk.h"
#include "main.h"
//----------------------------------------------------------------------
// FileHeader::Allocate
// Initialize a fresh file header for a newly created file.
// Allocate data blocks for the file out of the map of free disk blocks.
// Return FALSE if there are not enough free blocks to accomodate
// the new file.
//
// "freeMap" is the bit map of free disk sectors
// "fileSize" is the bit map of free disk sectors
//----------------------------------------------------------------------
bool
FileHeader::Allocate(PersistentBitmap *freeMap, int fileSize)
{
numBytes = fileSize;
numSectors = divRoundUp(fileSize, SectorSize);
if (freeMap->NumClear() < numSectors)
return FALSE; // not enough space
for (int i = 0; i < numSectors; i++) {
dataSectors[i] = freeMap->FindAndSet();
// since we checked that there was enough free space,
// we expect this to succeed
ASSERT(dataSectors[i] >= 0);
}
return TRUE;
}
//----------------------------------------------------------------------
// FileHeader::Deallocate
// De-allocate all the space allocated for data blocks for this file.
//
// "freeMap" is the bit map of free disk sectors
//----------------------------------------------------------------------
void
FileHeader::Deallocate(PersistentBitmap *freeMap)
{
for (int i = 0; i < numSectors; i++) {
ASSERT(freeMap->Test((int) dataSectors[i])); // ought to be marked!
freeMap->Clear((int) dataSectors[i]);
}
}
//----------------------------------------------------------------------
// FileHeader::FetchFrom
// Fetch contents of file header from disk.
//
// "sector" is the disk sector containing the file header
//----------------------------------------------------------------------
void
FileHeader::FetchFrom(int sector)
{
kernel->synchDisk->ReadSector(sector, (char *)this);
}
//----------------------------------------------------------------------
// FileHeader::WriteBack
// Write the modified contents of the file header back to disk.
//
// "sector" is the disk sector to contain the file header
//----------------------------------------------------------------------
void
FileHeader::WriteBack(int sector)
{
kernel->synchDisk->WriteSector(sector, (char *)this);
}
//----------------------------------------------------------------------
// FileHeader::ByteToSector
// Return which disk sector is storing a particular byte within the file.
// This is essentially a translation from a virtual address (the
// offset in the file) to a physical address (the sector where the
// data at the offset is stored).
//
// "offset" is the location within the file of the byte in question
//----------------------------------------------------------------------
int
FileHeader::ByteToSector(int offset)
{
return(dataSectors[offset / SectorSize]);
}
//----------------------------------------------------------------------
// FileHeader::FileLength
// Return the number of bytes in the file.
//----------------------------------------------------------------------
int
FileHeader::FileLength()
{
return numBytes;
}
//----------------------------------------------------------------------
// FileHeader::Print
// Print the contents of the file header, and the contents of all
// the data blocks pointed to by the file header.
//----------------------------------------------------------------------
void
FileHeader::Print()
{
int i, j, k;
char *data = new char[SectorSize];
printf("FileHeader contents. File size: %d. File blocks:\n", numBytes);
for (i = 0; i < numSectors; i++)
printf("%d ", dataSectors[i]);
printf("\nFile contents:\n");
for (i = k = 0; i < numSectors; i++) {
kernel->synchDisk->ReadSector(dataSectors[i], data);
for (j = 0; (j < SectorSize) && (k < numBytes); j++, k++) {
if ('\040' <= data[j] && data[j] <= '\176') // isprint(data[j])
printf("%c", data[j]);
else
printf("\\%x", (unsigned char)data[j]);
}
printf("\n");
}
delete [] data;
}

66
code/filesys/filehdr.h Executable file
View File

@@ -0,0 +1,66 @@
// filehdr.h
// Data structures for managing a disk file header.
//
// A file header describes where on disk to find the data in a file,
// along with other information about the file (for instance, its
// length, owner, etc.)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#ifndef FILEHDR_H
#define FILEHDR_H
#include "disk.h"
#include "pbitmap.h"
#define NumDirect ((SectorSize - 2 * sizeof(int)) / sizeof(int))
#define MaxFileSize (NumDirect * SectorSize)
// The following class defines the Nachos "file header" (in UNIX terms,
// the "i-node"), describing where on disk to find all of the data in the file.
// The file header is organized as a simple table of pointers to
// data blocks.
//
// The file header data structure can be stored in memory or on disk.
// When it is on disk, it is stored in a single sector -- this means
// that we assume the size of this data structure to be the same
// as one disk sector. Without indirect addressing, this
// limits the maximum file length to just under 4K bytes.
//
// There is no constructor; rather the file header can be initialized
// by allocating blocks for the file (if it is a new file), or by
// reading it from disk.
class FileHeader {
public:
bool Allocate(PersistentBitmap *bitMap, int fileSize);// Initialize a file header,
// including allocating space
// on disk for the file data
void Deallocate(PersistentBitmap *bitMap); // De-allocate this file's
// data blocks
void FetchFrom(int sectorNumber); // Initialize file header from disk
void WriteBack(int sectorNumber); // Write modifications to file header
// back to disk
int ByteToSector(int offset); // Convert a byte offset into the file
// to the disk sector containing
// the byte
int FileLength(); // Return the length of the file
// in bytes
void Print(); // Print the contents of the file.
private:
int numBytes; // Number of bytes in the file
int numSectors; // Number of data sectors in the file
int dataSectors[NumDirect]; // Disk sector numbers for each data
// block in the file
};
#endif // FILEHDR_H

340
code/filesys/filesys.cc Executable file
View File

@@ -0,0 +1,340 @@
// filesys.cc
// Routines to manage the overall operation of the file system.
// Implements routines to map from textual file names to files.
//
// Each file in the file system has:
// A file header, stored in a sector on disk
// (the size of the file header data structure is arranged
// to be precisely the size of 1 disk sector)
// A number of data blocks
// An entry in the file system directory
//
// The file system consists of several data structures:
// A bitmap of free disk sectors (cf. bitmap.h)
// A directory of file names and file headers
//
// Both the bitmap and the directory are represented as normal
// files. Their file headers are located in specific sectors
// (sector 0 and sector 1), so that the file system can find them
// on bootup.
//
// The file system assumes that the bitmap and directory files are
// kept "open" continuously while Nachos is running.
//
// For those operations (such as Create, Remove) that modify the
// directory and/or bitmap, if the operation succeeds, the changes
// are written immediately back to disk (the two files are kept
// open during all this time). If the operation fails, and we have
// modified part of the directory and/or bitmap, we simply discard
// the changed version, without writing it back to disk.
//
// Our implementation at this point has the following restrictions:
//
// there is no synchronization for concurrent accesses
// files have a fixed size, set when the file is created
// files cannot be bigger than about 3KB in size
// there is no hierarchical directory structure, and only a limited
// number of files can be added to the system
// there is no attempt to make the system robust to failures
// (if Nachos exits in the middle of an operation that modifies
// the file system, it may corrupt the disk)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef FILESYS_STUB
#include "copyright.h"
#include "debug.h"
#include "disk.h"
#include "pbitmap.h"
#include "directory.h"
#include "filehdr.h"
#include "filesys.h"
// Sectors containing the file headers for the bitmap of free sectors,
// and the directory of files. These file headers are placed in well-known
// sectors, so that they can be located on boot-up.
#define FreeMapSector 0
#define DirectorySector 1
// Initial file sizes for the bitmap and directory; until the file system
// supports extensible files, the directory size sets the maximum number
// of files that can be loaded onto the disk.
#define FreeMapFileSize (NumSectors / BitsInByte)
#define NumDirEntries 10
#define DirectoryFileSize (sizeof(DirectoryEntry) * NumDirEntries)
//----------------------------------------------------------------------
// FileSystem::FileSystem
// Initialize the file system. If format = TRUE, the disk has
// nothing on it, and we need to initialize the disk to contain
// an empty directory, and a bitmap of free sectors (with almost but
// not all of the sectors marked as free).
//
// If format = FALSE, we just have to open the files
// representing the bitmap and the directory.
//
// "format" -- should we initialize the disk?
//----------------------------------------------------------------------
FileSystem::FileSystem(bool format)
{
DEBUG(dbgFile, "Initializing the file system.");
if (format) {
PersistentBitmap *freeMap = new PersistentBitmap(NumSectors);
Directory *directory = new Directory(NumDirEntries);
FileHeader *mapHdr = new FileHeader;
FileHeader *dirHdr = new FileHeader;
DEBUG(dbgFile, "Formatting the file system.");
// First, allocate space for FileHeaders for the directory and bitmap
// (make sure no one else grabs these!)
freeMap->Mark(FreeMapSector);
freeMap->Mark(DirectorySector);
// Second, allocate space for the data blocks containing the contents
// of the directory and bitmap files. There better be enough space!
ASSERT(mapHdr->Allocate(freeMap, FreeMapFileSize));
ASSERT(dirHdr->Allocate(freeMap, DirectoryFileSize));
// Flush the bitmap and directory FileHeaders back to disk
// We need to do this before we can "Open" the file, since open
// reads the file header off of disk (and currently the disk has garbage
// on it!).
DEBUG(dbgFile, "Writing headers back to disk.");
mapHdr->WriteBack(FreeMapSector);
dirHdr->WriteBack(DirectorySector);
// OK to open the bitmap and directory files now
// The file system operations assume these two files are left open
// while Nachos is running.
freeMapFile = new OpenFile(FreeMapSector);
directoryFile = new OpenFile(DirectorySector);
// Once we have the files "open", we can write the initial version
// of each file back to disk. The directory at this point is completely
// empty; but the bitmap has been changed to reflect the fact that
// sectors on the disk have been allocated for the file headers and
// to hold the file data for the directory and bitmap.
DEBUG(dbgFile, "Writing bitmap and directory back to disk.");
freeMap->WriteBack(freeMapFile); // flush changes to disk
directory->WriteBack(directoryFile);
if (debug->IsEnabled('f')) {
freeMap->Print();
directory->Print();
}
delete freeMap;
delete directory;
delete mapHdr;
delete dirHdr;
} else {
// if we are not formatting the disk, just open the files representing
// the bitmap and directory; these are left open while Nachos is running
freeMapFile = new OpenFile(FreeMapSector);
directoryFile = new OpenFile(DirectorySector);
}
}
//----------------------------------------------------------------------
// FileSystem::Create
// Create a file in the Nachos file system (similar to UNIX create).
// Since we can't increase the size of files dynamically, we have
// to give Create the initial size of the file.
//
// The steps to create a file are:
// Make sure the file doesn't already exist
// Allocate a sector for the file header
// Allocate space on disk for the data blocks for the file
// Add the name to the directory
// Store the new file header on disk
// Flush the changes to the bitmap and the directory back to disk
//
// Return TRUE if everything goes ok, otherwise, return FALSE.
//
// Create fails if:
// file is already in directory
// no free space for file header
// no free entry for file in directory
// no free space for data blocks for the file
//
// Note that this implementation assumes there is no concurrent access
// to the file system!
//
// "name" -- name of file to be created
// "initialSize" -- size of file to be created
//----------------------------------------------------------------------
bool
FileSystem::Create(char *name, int initialSize)
{
Directory *directory;
PersistentBitmap *freeMap;
FileHeader *hdr;
int sector;
bool success;
DEBUG(dbgFile, "Creating file " << name << " size " << initialSize);
directory = new Directory(NumDirEntries);
directory->FetchFrom(directoryFile);
if (directory->Find(name) != -1)
success = FALSE; // file is already in directory
else {
freeMap = new PersistentBitmap(freeMapFile,NumSectors);
sector = freeMap->FindAndSet(); // find a sector to hold the file header
if (sector == -1)
success = FALSE; // no free block for file header
else if (!directory->Add(name, sector))
success = FALSE; // no space in directory
else {
hdr = new FileHeader;
if (!hdr->Allocate(freeMap, initialSize))
success = FALSE; // no space on disk for data
else {
success = TRUE;
// everthing worked, flush all changes back to disk
hdr->WriteBack(sector);
directory->WriteBack(directoryFile);
freeMap->WriteBack(freeMapFile);
}
delete hdr;
}
delete freeMap;
}
delete directory;
return success;
}
//----------------------------------------------------------------------
// FileSystem::Open
// Open a file for reading and writing.
// To open a file:
// Find the location of the file's header, using the directory
// Bring the header into memory
//
// "name" -- the text name of the file to be opened
//----------------------------------------------------------------------
OpenFile *
FileSystem::Open(char *name)
{
Directory *directory = new Directory(NumDirEntries);
OpenFile *openFile = NULL;
int sector;
DEBUG(dbgFile, "Opening file" << name);
directory->FetchFrom(directoryFile);
sector = directory->Find(name);
if (sector >= 0)
openFile = new OpenFile(sector); // name was found in directory
delete directory;
return openFile; // return NULL if not found
}
//----------------------------------------------------------------------
// FileSystem::Remove
// Delete a file from the file system. This requires:
// Remove it from the directory
// Delete the space for its header
// Delete the space for its data blocks
// Write changes to directory, bitmap back to disk
//
// Return TRUE if the file was deleted, FALSE if the file wasn't
// in the file system.
//
// "name" -- the text name of the file to be removed
//----------------------------------------------------------------------
bool
FileSystem::Remove(char *name)
{
Directory *directory;
PersistentBitmap *freeMap;
FileHeader *fileHdr;
int sector;
directory = new Directory(NumDirEntries);
directory->FetchFrom(directoryFile);
sector = directory->Find(name);
if (sector == -1) {
delete directory;
return FALSE; // file not found
}
fileHdr = new FileHeader;
fileHdr->FetchFrom(sector);
freeMap = new PersistentBitmap(freeMapFile,NumSectors);
fileHdr->Deallocate(freeMap); // remove data blocks
freeMap->Clear(sector); // remove header block
directory->Remove(name);
freeMap->WriteBack(freeMapFile); // flush to disk
directory->WriteBack(directoryFile); // flush to disk
delete fileHdr;
delete directory;
delete freeMap;
return TRUE;
}
//----------------------------------------------------------------------
// FileSystem::List
// List all the files in the file system directory.
//----------------------------------------------------------------------
void
FileSystem::List()
{
Directory *directory = new Directory(NumDirEntries);
directory->FetchFrom(directoryFile);
directory->List();
delete directory;
}
//----------------------------------------------------------------------
// FileSystem::Print
// Print everything about the file system:
// the contents of the bitmap
// the contents of the directory
// for each file in the directory,
// the contents of the file header
// the data in the file
//----------------------------------------------------------------------
void
FileSystem::Print()
{
FileHeader *bitHdr = new FileHeader;
FileHeader *dirHdr = new FileHeader;
PersistentBitmap *freeMap = new PersistentBitmap(freeMapFile,NumSectors);
Directory *directory = new Directory(NumDirEntries);
printf("Bit map file header:\n");
bitHdr->FetchFrom(FreeMapSector);
bitHdr->Print();
printf("Directory file header:\n");
dirHdr->FetchFrom(DirectorySector);
dirHdr->Print();
freeMap->Print();
directory->FetchFrom(directoryFile);
directory->Print();
delete bitHdr;
delete dirHdr;
delete freeMap;
delete directory;
}
#endif // FILESYS_STUB

98
code/filesys/filesys.h Executable file
View File

@@ -0,0 +1,98 @@
// filesys.h
// Data structures to represent the Nachos file system.
//
// A file system is a set of files stored on disk, organized
// into directories. Operations on the file system have to
// do with "naming" -- creating, opening, and deleting files,
// given a textual file name. Operations on an individual
// "open" file (read, write, close) are to be found in the OpenFile
// class (openfile.h).
//
// We define two separate implementations of the file system.
// The "STUB" version just re-defines the Nachos file system
// operations as operations on the native UNIX file system on the machine
// running the Nachos simulation.
//
// The other version is a "real" file system, built on top of
// a disk simulator. The disk is simulated using the native UNIX
// file system (in a file named "DISK").
//
// In the "real" implementation, there are two key data structures used
// in the file system. There is a single "root" directory, listing
// all of the files in the file system; unlike UNIX, the baseline
// system does not provide a hierarchical directory structure.
// In addition, there is a bitmap for allocating
// disk sectors. Both the root directory and the bitmap are themselves
// stored as files in the Nachos file system -- this causes an interesting
// bootstrap problem when the simulated disk is initialized.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef FS_H
#define FS_H
#include "copyright.h"
#include "sysdep.h"
#include "openfile.h"
#ifdef FILESYS_STUB // Temporarily implement file system calls as
// calls to UNIX, until the real file system
// implementation is available
class FileSystem {
public:
FileSystem() { for (int i = 0; i < 20; i++) fileDescriptorTable[i] = NULL; }
bool Create(char *name) {
int fileDescriptor = OpenForWrite(name);
if (fileDescriptor == -1) return FALSE;
Close(fileDescriptor);
return TRUE;
}
OpenFile* Open(char *name) {
int fileDescriptor = OpenForReadWrite(name, FALSE);
if (fileDescriptor == -1) return NULL;
return new OpenFile(fileDescriptor);
}
bool Remove(char *name) { return Unlink(name) == 0; }
OpenFile *fileDescriptorTable[20];
};
#else // FILESYS
class FileSystem {
public:
FileSystem(bool format); // Initialize the file system.
// Must be called *after* "synchDisk"
// has been initialized.
// If "format", there is nothing on
// the disk, so initialize the directory
// and the bitmap of free blocks.
bool Create(char *name, int initialSize);
// Create a file (UNIX creat)
OpenFile* Open(char *name); // Open a file (UNIX open)
bool Remove(char *name); // Delete a file (UNIX unlink)
void List(); // List all the files in the file system
void Print(); // List all the files and their contents
private:
OpenFile* freeMapFile; // Bit map of free disk blocks,
// represented as a file
OpenFile* directoryFile; // "Root" directory -- list of
// file names, represented as a file
};
#endif // FILESYS
#endif // FS_H

196
code/filesys/openfile.cc Executable file
View File

@@ -0,0 +1,196 @@
// openfile.cc
// Routines to manage an open Nachos file. As in UNIX, a
// file must be open before we can read or write to it.
// Once we're all done, we can close it (in Nachos, by deleting
// the OpenFile data structure).
//
// Also as in UNIX, for convenience, we keep the file header in
// memory while the file is open.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef FILESYS_STUB
#include "copyright.h"
#include "main.h"
#include "filehdr.h"
#include "openfile.h"
#include "synchdisk.h"
//----------------------------------------------------------------------
// OpenFile::OpenFile
// Open a Nachos file for reading and writing. Bring the file header
// into memory while the file is open.
//
// "sector" -- the location on disk of the file header for this file
//----------------------------------------------------------------------
OpenFile::OpenFile(int sector)
{
hdr = new FileHeader;
hdr->FetchFrom(sector);
seekPosition = 0;
}
//----------------------------------------------------------------------
// OpenFile::~OpenFile
// Close a Nachos file, de-allocating any in-memory data structures.
//----------------------------------------------------------------------
OpenFile::~OpenFile()
{
delete hdr;
}
//----------------------------------------------------------------------
// OpenFile::Seek
// Change the current location within the open file -- the point at
// which the next Read or Write will start from.
//
// "position" -- the location within the file for the next Read/Write
//----------------------------------------------------------------------
void
OpenFile::Seek(int position)
{
seekPosition = position;
}
//----------------------------------------------------------------------
// OpenFile::Read/Write
// Read/write a portion of a file, starting from seekPosition.
// Return the number of bytes actually written or read, and as a
// side effect, increment the current position within the file.
//
// Implemented using the more primitive ReadAt/WriteAt.
//
// "into" -- the buffer to contain the data to be read from disk
// "from" -- the buffer containing the data to be written to disk
// "numBytes" -- the number of bytes to transfer
//----------------------------------------------------------------------
int
OpenFile::Read(char *into, int numBytes)
{
int result = ReadAt(into, numBytes, seekPosition);
seekPosition += result;
return result;
}
int
OpenFile::Write(char *into, int numBytes)
{
int result = WriteAt(into, numBytes, seekPosition);
seekPosition += result;
return result;
}
//----------------------------------------------------------------------
// OpenFile::ReadAt/WriteAt
// Read/write a portion of a file, starting at "position".
// Return the number of bytes actually written or read, but has
// no side effects (except that Write modifies the file, of course).
//
// There is no guarantee the request starts or ends on an even disk sector
// boundary; however the disk only knows how to read/write a whole disk
// sector at a time. Thus:
//
// For ReadAt:
// We read in all of the full or partial sectors that are part of the
// request, but we only copy the part we are interested in.
// For WriteAt:
// We must first read in any sectors that will be partially written,
// so that we don't overwrite the unmodified portion. We then copy
// in the data that will be modified, and write back all the full
// or partial sectors that are part of the request.
//
// "into" -- the buffer to contain the data to be read from disk
// "from" -- the buffer containing the data to be written to disk
// "numBytes" -- the number of bytes to transfer
// "position" -- the offset within the file of the first byte to be
// read/written
//----------------------------------------------------------------------
int
OpenFile::ReadAt(char *into, int numBytes, int position)
{
int fileLength = hdr->FileLength();
int i, firstSector, lastSector, numSectors;
char *buf;
if ((numBytes <= 0) || (position >= fileLength))
return 0; // check request
if ((position + numBytes) > fileLength)
numBytes = fileLength - position;
DEBUG(dbgFile, "Reading " << numBytes << " bytes at " << position << " from file of length " << fileLength);
firstSector = divRoundDown(position, SectorSize);
lastSector = divRoundDown(position + numBytes - 1, SectorSize);
numSectors = 1 + lastSector - firstSector;
// read in all the full and partial sectors that we need
buf = new char[numSectors * SectorSize];
for (i = firstSector; i <= lastSector; i++)
kernel->synchDisk->ReadSector(hdr->ByteToSector(i * SectorSize),
&buf[(i - firstSector) * SectorSize]);
// copy the part we want
bcopy(&buf[position - (firstSector * SectorSize)], into, numBytes);
delete [] buf;
return numBytes;
}
int
OpenFile::WriteAt(char *from, int numBytes, int position)
{
int fileLength = hdr->FileLength();
int i, firstSector, lastSector, numSectors;
bool firstAligned, lastAligned;
char *buf;
if ((numBytes <= 0) || (position >= fileLength))
return 0; // check request
if ((position + numBytes) > fileLength)
numBytes = fileLength - position;
DEBUG(dbgFile, "Writing " << numBytes << " bytes at " << position << " from file of length " << fileLength);
firstSector = divRoundDown(position, SectorSize);
lastSector = divRoundDown(position + numBytes - 1, SectorSize);
numSectors = 1 + lastSector - firstSector;
buf = new char[numSectors * SectorSize];
firstAligned = (position == (firstSector * SectorSize));
lastAligned = ((position + numBytes) == ((lastSector + 1) * SectorSize));
// read in first and last sector, if they are to be partially modified
if (!firstAligned)
ReadAt(buf, SectorSize, firstSector * SectorSize);
if (!lastAligned && ((firstSector != lastSector) || firstAligned))
ReadAt(&buf[(lastSector - firstSector) * SectorSize],
SectorSize, lastSector * SectorSize);
// copy in the bytes we want to change
bcopy(from, &buf[position - (firstSector * SectorSize)], numBytes);
// write modified sectors back
for (i = firstSector; i <= lastSector; i++)
kernel->synchDisk->WriteSector(hdr->ByteToSector(i * SectorSize),
&buf[(i - firstSector) * SectorSize]);
delete [] buf;
return numBytes;
}
//----------------------------------------------------------------------
// OpenFile::Length
// Return the number of bytes in the file.
//----------------------------------------------------------------------
int
OpenFile::Length()
{
return hdr->FileLength();
}
#endif //FILESYS_STUB

97
code/filesys/openfile.h Executable file
View File

@@ -0,0 +1,97 @@
// openfile.h
// Data structures for opening, closing, reading and writing to
// individual files. The operations supported are similar to
// the UNIX ones -- type 'man open' to the UNIX prompt.
//
// There are two implementations. One is a "STUB" that directly
// turns the file operations into the underlying UNIX operations.
// (cf. comment in filesys.h).
//
// The other is the "real" implementation, that turns these
// operations into read and write disk sector requests.
// In this baseline implementation of the file system, we don't
// worry about concurrent accesses to the file system
// by different threads.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef OPENFILE_H
#define OPENFILE_H
#include "copyright.h"
#include "utility.h"
#include "sysdep.h"
#ifdef FILESYS_STUB // Temporarily implement calls to
// Nachos file system as calls to UNIX!
// See definitions listed under #else
class OpenFile {
public:
OpenFile(int f) { file = f; currentOffset = 0; } // open the file
~OpenFile() { Close(file); } // close the file
int ReadAt(char *into, int numBytes, int position) {
Lseek(file, position, 0);
return ReadPartial(file, into, numBytes);
}
int WriteAt(char *from, int numBytes, int position) {
Lseek(file, position, 0);
WriteFile(file, from, numBytes);
return numBytes;
}
int Read(char *into, int numBytes) {
int numRead = ReadAt(into, numBytes, currentOffset);
currentOffset += numRead;
return numRead;
}
int Write(char *from, int numBytes) {
int numWritten = WriteAt(from, numBytes, currentOffset);
currentOffset += numWritten;
return numWritten;
}
int Length() { Lseek(file, 0, 2); return Tell(file); }
private:
int file;
int currentOffset;
};
#else // FILESYS
class FileHeader;
class OpenFile {
public:
OpenFile(int sector); // Open a file whose header is located
// at "sector" on the disk
~OpenFile(); // Close the file
void Seek(int position); // Set the position from which to
// start reading/writing -- UNIX lseek
int Read(char *into, int numBytes); // Read/write bytes from the file,
// starting at the implicit position.
// Return the # actually read/written,
// and increment position in file.
int Write(char *from, int numBytes);
int ReadAt(char *into, int numBytes, int position);
// Read/write bytes from the file,
// bypassing the implicit position.
int WriteAt(char *from, int numBytes, int position);
int Length(); // Return the number of bytes in the
// file (this interface is simpler
// than the UNIX idiom -- lseek to
// end of file, tell, lseek back
private:
FileHeader *hdr; // Header for this file
int seekPosition; // Current position within the file
};
#endif // FILESYS
#endif // OPENFILE_H

79
code/filesys/pbitmap.cc Executable file
View File

@@ -0,0 +1,79 @@
// pbitmap.c
// Routines to manage a persistent bitmap -- a bitmap that is
// stored on disk.
//
// Copyright (c) 1992,1993,1995 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "pbitmap.h"
//----------------------------------------------------------------------
// PersistentBitmap::PersistentBitmap(int)
// Initialize a bitmap with "numItems" bits, so that every bit is clear.
// it can be added somewhere on a list.
//
// "numItems" is the number of bits in the bitmap.
//
// This constructor does not initialize the bitmap from a disk file
//----------------------------------------------------------------------
PersistentBitmap::PersistentBitmap(int numItems):Bitmap(numItems)
{
}
//----------------------------------------------------------------------
// PersistentBitmap::PersistentBitmap(OpenFile*,int)
// Initialize a persistent bitmap with "numItems" bits,
// so that every bit is clear.
//
// "numItems" is the number of bits in the bitmap.
// "file" refers to an open file containing the bitmap (written
// by a previous call to PersistentBitmap::WriteBack
//
// This constructor initializes the bitmap from a disk file
//----------------------------------------------------------------------
PersistentBitmap::PersistentBitmap(OpenFile *file, int numItems):Bitmap(numItems)
{
// map has already been initialized by the BitMap constructor,
// but we will just overwrite that with the contents of the
// map found in the file
file->ReadAt((char *)map, numWords * sizeof(unsigned), 0);
}
//----------------------------------------------------------------------
// PersistentBitmap::~PersistentBitmap
// De-allocate a persistent bitmap.
//----------------------------------------------------------------------
PersistentBitmap::~PersistentBitmap()
{
}
//----------------------------------------------------------------------
// PersistentBitmap::FetchFrom
// Initialize the contents of a persistent bitmap from a Nachos file.
//
// "file" is the place to read the bitmap from
//----------------------------------------------------------------------
void
PersistentBitmap::FetchFrom(OpenFile *file)
{
file->ReadAt((char *)map, numWords * sizeof(unsigned), 0);
}
//----------------------------------------------------------------------
// PersistentBitmap::WriteBack
// Store the contents of a persistent bitmap to a Nachos file.
//
// "file" is the place to write the bitmap to
//----------------------------------------------------------------------
void
PersistentBitmap::WriteBack(OpenFile *file)
{
file->WriteAt((char *)map, numWords * sizeof(unsigned), 0);
}

35
code/filesys/pbitmap.h Executable file
View File

@@ -0,0 +1,35 @@
// pbitmap.h
// Data structures defining a "persistent" bitmap -- a bitmap
// that can be stored and fetched off of disk
//
// A persistent bitmap can either be initialized from the disk
// when it is created, or it can be initialized later using
// the FetchFrom method
//
// Copyright (c) 1992,1993,1995 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef PBITMAP_H
#define PBITMAP_H
#include "copyright.h"
#include "bitmap.h"
#include "openfile.h"
// The following class defines a persistent bitmap. It inherits all
// the behavior of a bitmap (see bitmap.h), adding the ability to
// be read from and stored to the disk.
class PersistentBitmap : public Bitmap {
public:
PersistentBitmap(OpenFile *file,int numItems); //initialize bitmap from disk
PersistentBitmap(int numItems); // or don't...
~PersistentBitmap(); // deallocate bitmap
void FetchFrom(OpenFile *file); // read bitmap from the disk
void WriteBack(OpenFile *file); // write bitmap contents to disk
};
#endif // PBITMAP_H

94
code/filesys/synchdisk.cc Executable file
View File

@@ -0,0 +1,94 @@
// synchdisk.cc
// Routines to synchronously access the disk. The physical disk
// is an asynchronous device (disk requests return immediately, and
// an interrupt happens later on). This is a layer on top of
// the disk providing a synchronous interface (requests wait until
// the request completes).
//
// Use a semaphore to synchronize the interrupt handlers with the
// pending requests. And, because the physical disk can only
// handle one operation at a time, use a lock to enforce mutual
// exclusion.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "synchdisk.h"
//----------------------------------------------------------------------
// SynchDisk::SynchDisk
// Initialize the synchronous interface to the physical disk, in turn
// initializing the physical disk.
//
//----------------------------------------------------------------------
SynchDisk::SynchDisk()
{
semaphore = new Semaphore("synch disk", 0);
lock = new Lock("synch disk lock");
disk = new Disk(this);
}
//----------------------------------------------------------------------
// SynchDisk::~SynchDisk
// De-allocate data structures needed for the synchronous disk
// abstraction.
//----------------------------------------------------------------------
SynchDisk::~SynchDisk()
{
delete disk;
delete lock;
delete semaphore;
}
//----------------------------------------------------------------------
// SynchDisk::ReadSector
// Read the contents of a disk sector into a buffer. Return only
// after the data has been read.
//
// "sectorNumber" -- the disk sector to read
// "data" -- the buffer to hold the contents of the disk sector
//----------------------------------------------------------------------
void
SynchDisk::ReadSector(int sectorNumber, char* data)
{
lock->Acquire(); // only one disk I/O at a time
disk->ReadRequest(sectorNumber, data);
semaphore->P(); // wait for interrupt
lock->Release();
}
//----------------------------------------------------------------------
// SynchDisk::WriteSector
// Write the contents of a buffer into a disk sector. Return only
// after the data has been written.
//
// "sectorNumber" -- the disk sector to be written
// "data" -- the new contents of the disk sector
//----------------------------------------------------------------------
void
SynchDisk::WriteSector(int sectorNumber, char* data)
{
lock->Acquire(); // only one disk I/O at a time
disk->WriteRequest(sectorNumber, data);
semaphore->P(); // wait for interrupt
lock->Release();
}
//----------------------------------------------------------------------
// SynchDisk::CallBack
// Disk interrupt handler. Wake up any thread waiting for the disk
// request to finish.
//----------------------------------------------------------------------
void
SynchDisk::CallBack()
{
semaphore->V();
}

55
code/filesys/synchdisk.h Executable file
View File

@@ -0,0 +1,55 @@
// synchdisk.h
// Data structures to export a synchronous interface to the raw
// disk device.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#ifndef SYNCHDISK_H
#define SYNCHDISK_H
#include "disk.h"
#include "synch.h"
#include "callback.h"
// The following class defines a "synchronous" disk abstraction.
// As with other I/O devices, the raw physical disk is an asynchronous device --
// requests to read or write portions of the disk return immediately,
// and an interrupt occurs later to signal that the operation completed.
// (Also, the physical characteristics of the disk device assume that
// only one operation can be requested at a time).
//
// This class provides the abstraction that for any individual thread
// making a request, it waits around until the operation finishes before
// returning.
class SynchDisk : public CallBackObj {
public:
SynchDisk(); // Initialize a synchronous disk,
// by initializing the raw Disk.
~SynchDisk(); // De-allocate the synch disk data
void ReadSector(int sectorNumber, char* data);
// Read/write a disk sector, returning
// only once the data is actually read
// or written. These call
// Disk::ReadRequest/WriteRequest and
// then wait until the request is done.
void WriteSector(int sectorNumber, char* data);
void CallBack(); // Called by the disk device interrupt
// handler, to signal that the
// current disk operation is complete.
private:
Disk *disk; // Raw disk device
Semaphore *semaphore; // To synchronize requesting thread
// with the interrupt handler
Lock *lock; // Only one read/write request
// can be sent to the disk at a time
};
#endif // SYNCHDISK_H

191
code/lib/bitmap.cc Executable file
View File

@@ -0,0 +1,191 @@
// bitmap.cc
// Routines to manage a bitmap -- an array of bits each of which
// can be either on or off. Represented as an array of integers.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "debug.h"
#include "bitmap.h"
//----------------------------------------------------------------------
// BitMap::BitMap
// Initialize a bitmap with "numItems" bits, so that every bit is clear.
// it can be added somewhere on a list.
//
// "numItems" is the number of bits in the bitmap.
//----------------------------------------------------------------------
Bitmap::Bitmap(int numItems)
{
int i;
ASSERT(numItems > 0);
numBits = numItems;
numWords = divRoundUp(numBits, BitsInWord);
map = new unsigned int[numWords];
for (i = 0; i < numWords; i++) {
map[i] = 0; // initialize map to keep Purify happy
}
for (i = 0; i < numBits; i++) {
Clear(i);
}
}
//----------------------------------------------------------------------
// Bitmap::~Bitmap
// De-allocate a bitmap.
//----------------------------------------------------------------------
Bitmap::~Bitmap()
{
delete map;
}
//----------------------------------------------------------------------
// Bitmap::Set
// Set the "nth" bit in a bitmap.
//
// "which" is the number of the bit to be set.
//----------------------------------------------------------------------
void
Bitmap::Mark(int which)
{
ASSERT(which >= 0 && which < numBits);
map[which / BitsInWord] |= 1 << (which % BitsInWord);
ASSERT(Test(which));
}
//----------------------------------------------------------------------
// Bitmap::Clear
// Clear the "nth" bit in a bitmap.
//
// "which" is the number of the bit to be cleared.
//----------------------------------------------------------------------
void
Bitmap::Clear(int which)
{
ASSERT(which >= 0 && which < numBits);
map[which / BitsInWord] &= ~(1 << (which % BitsInWord));
ASSERT(!Test(which));
}
//----------------------------------------------------------------------
// Bitmap::Test
// Return TRUE if the "nth" bit is set.
//
// "which" is the number of the bit to be tested.
//----------------------------------------------------------------------
bool
Bitmap::Test(int which) const
{
ASSERT(which >= 0 && which < numBits);
if (map[which / BitsInWord] & (1 << (which % BitsInWord))) {
return TRUE;
} else {
return FALSE;
}
}
//----------------------------------------------------------------------
// Bitmap::FindAndSet
// Return the number of the first bit which is clear.
// As a side effect, set the bit (mark it as in use).
// (In other words, find and allocate a bit.)
//
// If no bits are clear, return -1.
//----------------------------------------------------------------------
int
Bitmap::FindAndSet()
{
for (int i = 0; i < numBits; i++) {
if (!Test(i)) {
Mark(i);
return i;
}
}
return -1;
}
//----------------------------------------------------------------------
// Bitmap::NumClear
// Return the number of clear bits in the bitmap.
// (In other words, how many bits are unallocated?)
//----------------------------------------------------------------------
int
Bitmap::NumClear() const
{
int count = 0;
for (int i = 0; i < numBits; i++) {
if (!Test(i)) {
count++;
}
}
return count;
}
//----------------------------------------------------------------------
// Bitmap::Print
// Print the contents of the bitmap, for debugging.
//
// Could be done in a number of ways, but we just print the #'s of
// all the bits that are set in the bitmap.
//----------------------------------------------------------------------
void
Bitmap::Print() const
{
cout << "Bitmap set:\n";
for (int i = 0; i < numBits; i++) {
if (Test(i)) {
cout << i << ", ";
}
}
cout << "\n";
}
//----------------------------------------------------------------------
// Bitmap::SelfTest
// Test whether this module is working.
//----------------------------------------------------------------------
void
Bitmap::SelfTest()
{
int i;
ASSERT(numBits >= BitsInWord); // bitmap must be big enough
ASSERT(NumClear() == numBits); // bitmap must be empty
ASSERT(FindAndSet() == 0);
Mark(31);
ASSERT(Test(0) && Test(31));
ASSERT(FindAndSet() == 1);
Clear(0);
Clear(1);
Clear(31);
for (i = 0; i < numBits; i++) {
Mark(i);
}
ASSERT(FindAndSet() == -1); // bitmap should be full!
for (i = 0; i < numBits; i++) {
Clear(i);
}
}

59
code/lib/bitmap.h Executable file
View File

@@ -0,0 +1,59 @@
// bitmap.h
// Data structures defining a bitmap -- an array of bits each of which
// can be either on or off.
//
// Represented as an array of unsigned integers, on which we do
// modulo arithmetic to find the bit we are interested in.
//
// The bitmap can be parameterized with with the number of bits being
// managed.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef BITMAP_H
#define BITMAP_H
#include "copyright.h"
#include "utility.h"
// Definitions helpful for representing a bitmap as an array of integers
const int BitsInByte = 8;
const int BitsInWord = sizeof(unsigned int) * BitsInByte;
// The following class defines a "bitmap" -- an array of bits,
// each of which can be independently set, cleared, and tested.
//
// Most useful for managing the allocation of the elements of an array --
// for instance, disk sectors, or main memory pages.
// Each bit represents whether the corresponding sector or page is
// in use or free.
class Bitmap {
public:
Bitmap(int numItems); // Initialize a bitmap, with "numItems" bits
// initially, all bits are cleared.
~Bitmap(); // De-allocate bitmap
void Mark(int which); // Set the "nth" bit
void Clear(int which); // Clear the "nth" bit
bool Test(int which) const; // Is the "nth" bit set?
int FindAndSet(); // Return the # of a clear bit, and as a side
// effect, set the bit.
// If no bits are clear, return -1.
int NumClear() const; // Return the number of clear bits
void Print() const; // Print contents of bitmap
void SelfTest(); // Test whether bitmap is working
protected:
int numBits; // number of bits in the bitmap
int numWords; // number of words of bitmap storage
// (rounded up if numBits is not a
// multiple of the number of bits in
// a word)
unsigned int *map; // bit storage
};
#endif // BITMAP_H

24
code/lib/copyright.h Executable file
View File

@@ -0,0 +1,24 @@
/*
Copyright (c) 1992-1996 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.
*/
#ifdef MAIN /* include the copyright message in every executable */
static char *copyright = "Copyright (c) 1992-1993 The Regents of the University of California. All rights reserved.";
#endif // MAIN

45
code/lib/debug.cc Executable file
View File

@@ -0,0 +1,45 @@
// debug.cc
// Debugging routines. Allows users to control whether to
// print DEBUG statements, based on a command line argument.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "utility.h"
#include "debug.h"
#include "string.h"
//----------------------------------------------------------------------
// Debug::Debug
// Initialize so that only DEBUG messages with a flag in flagList
// will be printed.
//
// If the flag is "+", we enable all DEBUG messages.
//
// "flagList" is a string of characters for whose DEBUG messages are
// to be enabled.
//----------------------------------------------------------------------
Debug::Debug(char *flagList)
{
enableFlags = flagList;
}
//----------------------------------------------------------------------
// Debug::IsEnabled
// Return TRUE if DEBUG messages with "flag" are to be printed.
//----------------------------------------------------------------------
bool
Debug::IsEnabled(char flag)
{
if (enableFlags != NULL) {
return ((strchr(enableFlags, flag) != 0)
|| (strchr(enableFlags, '+') != 0));
} else {
return FALSE;
}
}

96
code/lib/debug.h Executable file
View File

@@ -0,0 +1,96 @@
// debug.h
// Data structures for debugging routines.
//
// The debugging routines allow the user to turn on selected
// debugging messages, controllable from the command line arguments
// passed to Nachos (-d). You are encouraged to add your own
// debugging flags. Please....
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef DEBUG_H
#define DEBUG_H
#include "copyright.h"
#include "utility.h"
#include "sysdep.h"
// The pre-defined debugging flags are:
const char dbgAll = '+'; // turn on all debug messages
const char dbgThread = 't'; // threads
const char dbgSynch = 's'; // locks, semaphores, condition vars
const char dbgInt = 'i'; // interrupt emulation
const char dbgMach = 'm'; // machine emulation
const char dbgDisk = 'd'; // disk emulation
const char dbgFile = 'f'; // file system
const char dbgAddr = 'a'; // address spaces
const char dbgNet = 'n'; // network emulation
const char dbgSys = 'u'; // systemcall
class Debug {
public:
Debug(char *flagList);
bool IsEnabled(char flag);
private:
char *enableFlags; // controls which DEBUG messages are printed
};
extern Debug *debug;
//----------------------------------------------------------------------
// DEBUG
// If flag is enabled, print a message.
//----------------------------------------------------------------------
#define DEBUG(flag,expr) \
if (!debug->IsEnabled(flag)) {} else { \
cerr << expr << "\n"; \
}
//----------------------------------------------------------------------
// ASSERT
// If condition is false, print a message and dump core.
// Useful for documenting assumptions in the code.
//
// NOTE: needs to be a #define, to be able to print the location
// where the error occurred.
//----------------------------------------------------------------------
#define ASSERT(condition) \
if (condition) {} else { \
cerr << "Assertion failed: line " << __LINE__ << " file " << __FILE__ << "\n"; \
Abort(); \
}
//----------------------------------------------------------------------
// ASSERTNOTREACHED
// Print a message and dump core (equivalent to ASSERT(FALSE) without
// making the compiler whine). Useful for documenting when
// code should not be reached.
//
// NOTE: needs to be a #define, to be able to print the location
// where the error occurred.
//----------------------------------------------------------------------
#define ASSERTNOTREACHED() \
{ \
cerr << "Assertion failed: line " << __LINE__ << " file " << __FILE__ << "\n"; \
Abort(); \
}
//----------------------------------------------------------------------
// ASSERTUNIMPLEMENTED
// Print a message that unimplemented code is executed and dump core
//----------------------------------------------------------------------
#define UNIMPLEMENTED() \
{ \
cerr << "Reached UNIMPLEMENTED function " << __FUNCTION__ << " in file: " \
<< __FILE__ << " line: " << __LINE__ << ".\n"; \
}
#endif // DEBUG_H

360
code/lib/hash.cc Executable file
View File

@@ -0,0 +1,360 @@
// hash.cc
// Routines to manage a self-expanding hash table of arbitrary things.
// The hashing function is supplied by the objects being put into
// the table; we use chaining to resolve hash conflicts.
//
// The hash table is implemented as an array of sorted lists,
// and we expand the hash table if the number of elements in the table
// gets too big.
//
// NOTE: Mutual exclusion must be provided by the caller.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
const int InitialBuckets = 4; // how big a hash table do we start with
const int ResizeRatio = 3; // when do we grow the hash table?
const int IncreaseSizeBy = 4; // how much do we grow table when needed?
#include "copyright.h"
//----------------------------------------------------------------------
// HashTable<Key,T>::HashTable
// Initialize a hash table, empty to start with.
// Elements can now be added to the table.
//----------------------------------------------------------------------
template <class Key, class T>
HashTable<Key,T>::HashTable(Key (*get)(T x), unsigned (*hFunc)(Key x))
{
numItems = 0;
InitBuckets(InitialBuckets);
getKey = get;
hash = hFunc;
}
//----------------------------------------------------------------------
// HashTable<Key,T>::InitBuckets
// Initialize the bucket array for a hash table.
// Called by the constructor and by ReHash().
//----------------------------------------------------------------------
template <class Key, class T>
void
HashTable<Key,T>::InitBuckets(int sz)
{
numBuckets = sz;
buckets = new Bucket[numBuckets];
for (int i = 0; i < sz; i++) {
buckets[i] = new List<T>;
}
}
//----------------------------------------------------------------------
// HashTable<T>::~HashTable
// Prepare a hash table for deallocation.
//----------------------------------------------------------------------
template <class Key, class T>
HashTable<Key,T>::~HashTable()
{
ASSERT(IsEmpty()); // make sure table is empty
DeleteBuckets(buckets, numBuckets);
}
//----------------------------------------------------------------------
// HashTable<Key,T>::DeleteBuckets
// De-Initialize the bucket array for a hash table.
// Called by the destructor and by ReHash().
//----------------------------------------------------------------------
template <class Key, class T>
void
HashTable<Key,T>::DeleteBuckets(List<T> **table, int sz)
{
for (int i = 0; i < sz; i++) {
delete table[i];
}
delete [] table;
}
//----------------------------------------------------------------------
// HashTable<Key,T>::HashValue
// Return hash table bucket that would contain key.
//----------------------------------------------------------------------
template <class Key, class T>
int
HashTable<Key, T>::HashValue(Key key) const
{
int result = (*hash)(key) % numBuckets;
ASSERT(result >= 0 && result < numBuckets);
return result;
}
//----------------------------------------------------------------------
// HashTable<Key,T>::Insert
// Put an item into the hashtable.
//
// Resize the table if the # of elements / # of buckets is too big.
// Then allocate a HashElement to keep track of the key, item pair,
// and add it to the right bucket.
//
// "key" is the key we'll use to find this item.
// "item" is the thing to put in the table.
//----------------------------------------------------------------------
template <class Key, class T>
void
HashTable<Key,T>::Insert(T item)
{
Key key = getKey(item);
ASSERT(!IsInTable(key));
if ((numItems / numBuckets) >= ResizeRatio) {
ReHash();
}
buckets[HashValue(key)]->Append(item);
numItems++;
ASSERT(IsInTable(key));
}
//----------------------------------------------------------------------
// HashTable<Key,T>::ReHash
// Increase the size of the hashtable, by
// (i) making a new table
// (ii) moving all the elements into the new table
// (iii) deleting the old table
//----------------------------------------------------------------------
template <class Key, class T>
void
HashTable<Key,T>::ReHash()
{
Bucket *oldTable = buckets;
int oldSize = numBuckets;
T item;
SanityCheck();
InitBuckets(numBuckets * IncreaseSizeBy);
for (int i = 0; i < oldSize; i++) {
while (!oldTable[i]->IsEmpty()) {
item = oldTable[i]->RemoveFront();
buckets[HashValue(getKey(item))]->Append(item);
}
}
DeleteBuckets(oldTable, oldSize);
SanityCheck();
}
//----------------------------------------------------------------------
// HashTable<Key,T>::FindInBucket
// Find an item in a hash table bucket, from it's key
//
// "bucket" -- the list storing the item, if it's in the table
// "key" -- the key uniquely identifying the item
//
// Returns:
// Whether item is found, and if found, the item.
//----------------------------------------------------------------------
template <class Key, class T>
bool
HashTable<Key,T>::FindInBucket(int bucket,
Key key, T *itemPtr) const
{
ListIterator<T> iterator(buckets[bucket]);
for (; !iterator.IsDone(); iterator.Next()) {
if (key == getKey(iterator.Item())) { // found!
*itemPtr = iterator.Item();
return TRUE;
}
}
*itemPtr = NULL;
return FALSE;
}
//----------------------------------------------------------------------
// HashTable<Key,T>::Find
// Find an item from the hash table.
//
// Returns:
// The item or NULL if not found.
//----------------------------------------------------------------------
template <class Key, class T>
bool
HashTable<Key,T>::Find(Key key, T *itemPtr) const
{
int bucket = HashValue(key);
return FindInBucket(bucket, key, itemPtr);
}
//----------------------------------------------------------------------
// HashTable<Key,T>::Remove
// Remove an item from the hash table. The item must be in the table.
//
// Returns:
// The removed item.
//----------------------------------------------------------------------
template <class Key, class T>
T
HashTable<Key,T>::Remove(Key key)
{
int bucket = HashValue(key);
T item;
bool found = FindInBucket(bucket, key, &item);
ASSERT(found); // item must be in table
buckets[bucket]->Remove(item);
numItems--;
ASSERT(!IsInTable(key));
return item;
}
//----------------------------------------------------------------------
// HashTable<Key,T>::Apply
// Apply function to every item in the hash table.
//
// "func" -- the function to apply
//----------------------------------------------------------------------
template <class Key,class T>
void
HashTable<Key,T>::Apply(void (*func)(T)) const
{
for (int bucket = 0; bucket < numBuckets; bucket++) {
buckets[bucket]->Apply(func);
}
}
//----------------------------------------------------------------------
// HashTable<Key,T>::FindNextFullBucket
// Find the next bucket in the hash table that has any items in it.
//
// "bucket" -- where to start looking for full buckets
//----------------------------------------------------------------------
template <class Key,class T>
int
HashTable<Key,T>::FindNextFullBucket(int bucket) const
{
for (; bucket < numBuckets; bucket++) {
if (!buckets[bucket]->IsEmpty()) {
break;
}
}
return bucket;
}
//----------------------------------------------------------------------
// HashTable<Key,T>::SanityCheck
// Test whether this is still a legal hash table.
//
// Tests: are all the buckets legal?
// does the table have the right # of elements?
// do all the elements hash to where they are stored?
//----------------------------------------------------------------------
template <class Key, class T>
void
HashTable<Key,T>::SanityCheck() const
{
int numFound = 0;
ListIterator<T> *iterator;
for (int i = 0; i < numBuckets; i++) {
buckets[i]->SanityCheck();
numFound += buckets[i]->NumInList();
iterator = new ListIterator<T>(buckets[i]);
for (; !iterator->IsDone(); iterator->Next()) {
ASSERT(i == HashValue(getKey(iterator->Item())));
}
delete iterator;
}
ASSERT(numItems == numFound);
}
//----------------------------------------------------------------------
// HashTable<Key,T>::SelfTest
// Test whether this module is working.
//----------------------------------------------------------------------
template <class Key, class T>
void
HashTable<Key,T>::SelfTest(T *p, int numEntries)
{
int i;
HashIterator<Key, T> *iterator = new HashIterator<Key,T>(this);
SanityCheck();
ASSERT(IsEmpty()); // check that table is empty in various ways
for (; !iterator->IsDone(); iterator->Next()) {
ASSERTNOTREACHED();
}
delete iterator;
for (i = 0; i < numEntries; i++) {
Insert(p[i]);
ASSERT(IsInTable(getKey(p[i])));
ASSERT(!IsEmpty());
}
// should be able to get out everything we put in
for (i = 0; i < numEntries; i++) {
ASSERT(Remove(getKey(p[i])) == p[i]);
}
ASSERT(IsEmpty());
SanityCheck();
}
//----------------------------------------------------------------------
// HashIterator<Key,T>::HashIterator
// Initialize a data structure to allow us to step through
// every entry in a has table.
//----------------------------------------------------------------------
template <class Key, class T>
HashIterator<Key,T>::HashIterator(HashTable<Key,T> *tbl)
{
table = tbl;
bucket = table->FindNextFullBucket(0);
bucketIter = NULL;
if (bucket < table->numBuckets) {
bucketIter = new ListIterator<T>(table->buckets[bucket]);
}
}
//----------------------------------------------------------------------
// HashIterator<Key,T>::Next
// Update iterator to point to the next item in the table.
//----------------------------------------------------------------------
template <class Key,class T>
void
HashIterator<Key,T>::Next()
{
bucketIter->Next();
if (bucketIter->IsDone()) {
delete bucketIter;
bucketIter = NULL;
bucket = table->FindNextFullBucket(++bucket);
if (bucket < table->numBuckets) {
bucketIter = new ListIterator<T>(table->buckets[bucket]);
}
}
}

123
code/lib/hash.h Executable file
View File

@@ -0,0 +1,123 @@
// hash.h
// Data structures to manage a hash table to relate arbitrary
// keys to arbitrary values. A hash table allows efficient lookup
// for the value given the key.
//
// I've only tested this implementation when both the key and the
// value are primitive types (ints or pointers). There is no
// guarantee that it will work in general. In particular, it
// assumes that the "==" operator works for both keys and values.
//
// In addition, the key must have Hash() defined:
// unsigned Hash(Key k);
// returns a randomized # based on value of key
//
// The value must have a function defined to retrieve the key:
// Key GetKey(T x);
//
// The hash table automatically resizes itself as items are
// put into the table. The implementation uses chaining
// to resolve hash conflicts.
//
// Allocation and deallocation of the items in the table are to
// be done by the caller.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef HASH_H
#define HASH_H
#include "copyright.h"
#include "list.h"
// The following class defines a "hash table" -- allowing quick
// lookup according to the hash function defined for the items
// being put into the table.
template <class Key,class T> class HashIterator;
template <class Key, class T>
class HashTable {
public:
HashTable(Key (*get)(T x), unsigned (*hFunc)(Key x));
// initialize a hash table
~HashTable(); // deallocate a hash table
void Insert(T item); // Put item into hash table
T Remove(Key key); // Remove item from hash table.
bool Find(Key key, T *itemPtr) const;
// Find an item from its key
bool IsInTable(Key key) { T dummy; return Find(key, &dummy); }
// Is the item in the table?
bool IsEmpty() { return numItems == 0; }
// does the table have anything in it
void Apply(void (*f)(T)) const;
// apply function to all elements in table
void SanityCheck() const;// is this still a legal hash table?
void SelfTest(T *p, int numItems);
// is the module working?
private:
typedef List<T> *Bucket;
Bucket *buckets; // the array of hash buckets
int numBuckets; // the number of buckets
int numItems; // the number of items in the table
Key (*getKey)(T x); // get Key from value
unsigned (*hash)(Key x); // the hash function
void InitBuckets(int size);// initialize bucket array
void DeleteBuckets(Bucket *table, int size);
// deallocate bucket array
int HashValue(Key key) const;
// which bucket does the key hash to?
void ReHash(); // expand the hash table
bool FindInBucket(int bucket, Key key, T *itemPtr) const;
// find item in bucket
int FindNextFullBucket(int start) const;
// find next full bucket starting from this one
friend class HashIterator<Key,T>;
};
// The following class can be used to step through a hash table --
// same interface as ListIterator. Example code:
// HashIterator<Key, T> iter(table);
//
// for (; !iter->IsDone(); iter->Next()) {
// Operation on iter->Item()
// }
template <class Key,class T>
class HashIterator {
public:
HashIterator(HashTable<Key,T> *table); // initialize an iterator
~HashIterator() { if (bucketIter != NULL) delete bucketIter;};
// destruct an iterator
bool IsDone() { return (bucket == table->numBuckets); };
// return TRUE if no more items in table
T Item() { ASSERT(!IsDone()); return bucketIter->Item(); };
// return current item in table
void Next(); // update iterator to point to next
private:
HashTable<Key,T> *table; // the hash table we're stepping through
int bucket; // current bucket we are in
ListIterator<T> *bucketIter; // where we are in the bucket
};
#include "hash.cc" // templates are really like macros
// so needs to be included in every
// file that uses the template
#endif // HASH_H

85
code/lib/libtest.cc Executable file
View File

@@ -0,0 +1,85 @@
// libtest.cc
// Driver code to call self-test routines for standard library
// classes -- bitmaps, lists, sorted lists, and hash tables.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "libtest.h"
#include "bitmap.h"
#include "list.h"
#include "hash.h"
#include "sysdep.h"
//----------------------------------------------------------------------
// IntCompare
// Compare two integers together. Serves as the comparison
// function for testing SortedLists
//----------------------------------------------------------------------
static int
IntCompare(int x, int y) {
if (x < y) return -1;
else if (x == y) return 0;
else return 1;
}
//----------------------------------------------------------------------
// HashInt, HashKey
// Compute a hash function on an integer. Serves as the
// hashing function for testing HashTables.
//----------------------------------------------------------------------
static unsigned int
HashInt(int key) {
return (unsigned int) key;
}
//----------------------------------------------------------------------
// HashKey
// Convert a string into an integer. Serves as the function
// to retrieve the key from the item in the hash table, for
// testing HashTables. Should be able to use "atoi" directly,
// but some compilers complain about that.
//----------------------------------------------------------------------
static int
HashKey(char *str) {
return atoi(str);
}
// Array of values to be inserted into a List or SortedList.
static int listTestVector[] = { 9, 5, 7 };
// Array of values to be inserted into the HashTable
// There are enough here to force a ReHash().
static char *hashTestVector[] = { "0", "1", "2", "3", "4", "5", "6",
"7", "8", "9", "10", "11", "12", "13", "14"};
//----------------------------------------------------------------------
// LibSelfTest
// Run self tests on bitmaps, lists, sorted lists, and
// hash tables.
//----------------------------------------------------------------------
void
LibSelfTest () {
Bitmap *map = new Bitmap(200);
List<int> *list = new List<int>;
SortedList<int> *sortList = new SortedList<int>(IntCompare);
HashTable<int, char *> *hashTable =
new HashTable<int, char *>(HashKey, HashInt);
map->SelfTest();
list->SelfTest(listTestVector, sizeof(listTestVector)/sizeof(int));
sortList->SelfTest(listTestVector, sizeof(listTestVector)/sizeof(int));
hashTable->SelfTest(hashTestVector, sizeof(hashTestVector)/sizeof(char *));
delete map;
delete list;
delete sortList;
delete hashTable;
}

15
code/lib/libtest.h Executable file
View File

@@ -0,0 +1,15 @@
// libtest.h
// Defines self test module for standard library routines.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef LIBTEST_H
#define LIBTEST_H
#include "copyright.h"
extern void LibSelfTest();
#endif // LIBTEST_H

383
code/lib/list.cc Executable file
View File

@@ -0,0 +1,383 @@
// list.cc
// Routines to manage a singly linked list of "things".
// Lists are implemented as templates so that we can store
// anything on the list in a type-safe manner.
//
// A "ListElement" is allocated for each item to be put on the
// list; it is de-allocated when the item is removed. This means
// we don't need to keep a "next" pointer in every object we
// want to put on a list.
//
// NOTE: Mutual exclusion must be provided by the caller.
// If you want a synchronized list, you must use the routines
// in synchlist.cc.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
//----------------------------------------------------------------------
// ListElement<T>::ListElement
// Initialize a list element, so it can be added somewhere on a list.
//
// "itm" is the thing to be put on the list.
//----------------------------------------------------------------------
template <class T>
ListElement<T>::ListElement(T itm)
{
item = itm;
next = NULL; // always initialize to something!
}
//----------------------------------------------------------------------
// List<T>::List
// Initialize a list, empty to start with.
// Elements can now be added to the list.
//----------------------------------------------------------------------
template <class T>
List<T>::List()
{
first = last = NULL;
numInList = 0;
}
//----------------------------------------------------------------------
// List<T>::~List
// Prepare a list for deallocation.
// This does *NOT* free list elements, nor does it
// free the data those elements point to.
// Normally, the list should be empty when this is called.
//----------------------------------------------------------------------
template <class T>
List<T>::~List()
{
}
//----------------------------------------------------------------------
// List<T>::Append
// Append an "item" to the end of the list.
//
// Allocate a ListElement to keep track of the item.
// If the list is empty, then this will be the only element.
// Otherwise, put it at the end.
//
// "item" is the thing to put on the list.
//----------------------------------------------------------------------
template <class T>
void
List<T>::Append(T item)
{
ListElement<T> *element = new ListElement<T>(item);
ASSERT(!this->IsInList(item));
if (IsEmpty()) { // list is empty
first = element;
last = element;
} else { // else put it after last
last->next = element;
last = element;
}
numInList++;
ASSERT(this->IsInList(item));
}
//----------------------------------------------------------------------
// List<T>::Prepend
// Same as Append, only put "item" on the front.
//----------------------------------------------------------------------
template <class T>
void
List<T>::Prepend(T item)
{
ListElement<T> *element = new ListElement<T>(item);
ASSERT(!this->IsInList(item));
if (IsEmpty()) { // list is empty
first = element;
last = element;
} else { // else put it before first
element->next = first;
first = element;
}
numInList++;
ASSERT(this->IsInList(item));
}
//----------------------------------------------------------------------
// List<T>::RemoveFront
// Remove the first "item" from the front of the list.
// List must not be empty.
//
// Returns:
// The removed item.
//----------------------------------------------------------------------
template <class T>
T
List<T>::RemoveFront()
{
ListElement<T> *element = first;
T thing;
ASSERT(!IsEmpty());
thing = first->item;
if (first == last) { // list had one item, now has none
first = NULL;
last = NULL;
} else {
first = element->next;
}
numInList--;
delete element;
return thing;
}
//----------------------------------------------------------------------
// List<T>::Remove
// Remove a specific item from the list. Must be in the list!
//----------------------------------------------------------------------
template <class T>
void
List<T>::Remove(T item)
{
ListElement<T> *prev, *ptr;
T removed;
ASSERT(this->IsInList(item));
// if first item on list is match, then remove from front
if (item == first->item) {
removed = RemoveFront();
ASSERT(item == removed);
} else {
prev = first;
for (ptr = first->next; ptr != NULL; prev = ptr, ptr = ptr->next) {
if (item == ptr->item) {
prev->next = ptr->next;
if (prev->next == NULL) {
last = prev;
}
delete ptr;
numInList--;
break;
}
}
ASSERT(ptr != NULL); // should always find item!
}
ASSERT(!this->IsInList(item));
}
//----------------------------------------------------------------------
// List<T>::IsInList
// Return TRUE if the item is in the list.
//----------------------------------------------------------------------
template <class T>
bool
List<T>::IsInList(T item) const
{
ListElement<T> *ptr;
for (ptr = first; ptr != NULL; ptr = ptr->next) {
if (item == ptr->item) {
return TRUE;
}
}
return FALSE;
}
//----------------------------------------------------------------------
// List<T>::Apply
// Apply function to every item on a list.
//
// "func" -- the function to apply
//----------------------------------------------------------------------
template <class T>
void
List<T>::Apply(void (*func)(T)) const
{
ListElement<T> *ptr;
for (ptr = first; ptr != NULL; ptr = ptr->next) {
(*func)(ptr->item);
}
}
//----------------------------------------------------------------------
// SortedList::Insert
// Insert an "item" into a list, so that the list elements are
// sorted in increasing order.
//
// Allocate a ListElement to keep track of the item.
// If the list is empty, then this will be the only element.
// Otherwise, walk through the list, one element at a time,
// to find where the new item should be placed.
//
// "item" is the thing to put on the list.
//----------------------------------------------------------------------
template <class T>
void
SortedList<T>::Insert(T item)
{
ListElement<T> *element = new ListElement<T>(item);
ListElement<T> *ptr; // keep track
ASSERT(!this->IsInList(item));
if (this->IsEmpty()) { // if list is empty, put at front
this->first = element;
this->last = element;
} else if (compare(item, this->first->item) < 0) { // item goes at front
element->next = this->first;
this->first = element;
} else { // look for first elt in list bigger than item
for (ptr = this->first; ptr->next != NULL; ptr = ptr->next) {
if (compare(item, ptr->next->item) < 0) {
element->next = ptr->next;
ptr->next = element;
this->numInList++;
return;
}
}
this->last->next = element; // item goes at end of list
this->last = element;
}
this->numInList++;
ASSERT(this->IsInList(item));
}
//----------------------------------------------------------------------
// List::SanityCheck
// Test whether this is still a legal list.
//
// Tests: do I get to last starting from first?
// does the list have the right # of elements?
//----------------------------------------------------------------------
template <class T>
void
List<T>::SanityCheck() const
{
ListElement<T> *ptr;
int numFound;
if (first == NULL) {
ASSERT((numInList == 0) && (last == NULL));
} else if (first == last) {
ASSERT((numInList == 1) && (last->next == NULL));
} else {
for (numFound = 1, ptr = first; ptr != last; ptr = ptr->next) {
numFound++;
ASSERT(numFound <= numInList); // prevent infinite loop
}
ASSERT(numFound == numInList);
ASSERT(last->next == NULL);
}
}
//----------------------------------------------------------------------
// List::SelfTest
// Test whether this module is working.
//----------------------------------------------------------------------
template <class T>
void
List<T>::SelfTest(T *p, int numEntries)
{
int i;
ListIterator<T> *iterator = new ListIterator<T>(this);
SanityCheck();
// check various ways that list is empty
ASSERT(IsEmpty() && (first == NULL));
for (; !iterator->IsDone(); iterator->Next()) {
ASSERTNOTREACHED(); // nothing on list
}
for (i = 0; i < numEntries; i++) {
Append(p[i]);
ASSERT(this->IsInList(p[i]));
ASSERT(!IsEmpty());
}
SanityCheck();
// should be able to get out everything we put in
for (i = 0; i < numEntries; i++) {
Remove(p[i]);
ASSERT(!this->IsInList(p[i]));
}
ASSERT(IsEmpty());
SanityCheck();
delete iterator;
}
//----------------------------------------------------------------------
// SortedList::SanityCheck
// Test whether this is still a legal sorted list.
//
// Test: is the list sorted?
//----------------------------------------------------------------------
template <class T>
void
SortedList<T>::SanityCheck() const
{
ListElement<T> *prev, *ptr;
List<T>::SanityCheck();
if (this->first != this->last) {
for (prev = this->first, ptr = this->first->next; ptr != NULL;
prev = ptr, ptr = ptr->next) {
ASSERT(compare(prev->item, ptr->item) <= 0);
}
}
}
//----------------------------------------------------------------------
// SortedList::SelfTest
// Test whether this module is working.
//----------------------------------------------------------------------
template <class T>
void
SortedList<T>::SelfTest(T *p, int numEntries)
{
int i;
T *q = new T[numEntries];
List<T>::SelfTest(p, numEntries);
for (i = 0; i < numEntries; i++) {
Insert(p[i]);
ASSERT(this->IsInList(p[i]));
}
SanityCheck();
// should be able to get out everything we put in
for (i = 0; i < numEntries; i++) {
q[i] = this->RemoveFront();
ASSERT(!this->IsInList(q[i]));
}
ASSERT(this->IsEmpty());
// make sure everything came out in the right order
for (i = 0; i < (numEntries - 1); i++) {
ASSERT(compare(q[i], q[i + 1]) <= 0);
}
SanityCheck();
delete q;
}

142
code/lib/list.h Executable file
View File

@@ -0,0 +1,142 @@
// list.h
// Data structures to manage LISP-like lists.
//
// As in LISP, a list can contain any type of data structure
// as an item on the list: thread control blocks,
// pending interrupts, etc. Allocation and deallocation of the
// items on the list are to be done by the caller.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef LIST_H
#define LIST_H
#include "copyright.h"
#include "debug.h"
// The following class defines a "list element" -- which is
// used to keep track of one item on a list. It is equivalent to a
// LISP cell, with a "car" ("next") pointing to the next element on the list,
// and a "cdr" ("item") pointing to the item on the list.
//
// This class is private to this module (and classes that inherit
// from this module). Made public for notational convenience.
template <class T>
class ListElement {
public:
ListElement(T itm); // initialize a list element
ListElement *next; // next element on list, NULL if this is last
T item; // item on the list
};
// The following class defines a "list" -- a singly linked list of
// list elements, each of which points to a single item on the list.
// The class has been tested only for primitive types (ints, pointers);
// no guarantees it will work in general. For instance, all types
// to be inserted into a list must have a "==" operator defined.
template <class T> class ListIterator;
template <class T>
class List {
public:
List(); // initialize the list
virtual ~List(); // de-allocate the list
virtual void Prepend(T item);// Put item at the beginning of the list
virtual void Append(T item); // Put item at the end of the list
T Front() { return first->item; }
// Return first item on list
// without removing it
T RemoveFront(); // Take item off the front of the list
void Remove(T item); // Remove specific item from list
bool IsInList(T item) const;// is the item in the list?
unsigned int NumInList() { return numInList;};
// how many items in the list?
bool IsEmpty() { return (numInList == 0); };
// is the list empty?
void Apply(void (*f)(T)) const;
// apply function to all elements in list
virtual void SanityCheck() const;
// has this list been corrupted?
void SelfTest(T *p, int numEntries);
// verify module is working
protected:
ListElement<T> *first; // Head of the list, NULL if list is empty
ListElement<T> *last; // Last element of list
int numInList; // number of elements in list
friend class ListIterator<T>;
};
// The following class defines a "sorted list" -- a singly linked list of
// list elements, arranged so that "Remove" always returns the smallest
// element.
// All types to be inserted onto a sorted list must have a "Compare"
// function defined:
// int Compare(T x, T y)
// returns -1 if x < y
// returns 0 if x == y
// returns 1 if x > y
template <class T>
class SortedList : public List<T> {
public:
SortedList(int (*comp)(T x, T y)) : List<T>() { compare = comp;};
~SortedList() {}; // base class destructor called automatically
void Insert(T item); // insert an item onto the list in sorted order
void SanityCheck() const; // has this list been corrupted?
void SelfTest(T *p, int numEntries);
// verify module is working
private:
int (*compare)(T x, T y); // function for sorting list elements
void Prepend(T item) { Insert(item); } // *pre*pending has no meaning
// in a sorted list
void Append(T item) { Insert(item); } // neither does *ap*pend
};
// The following class can be used to step through a list.
// Example code:
// ListIterator<T> *iter(list);
//
// for (; !iter->IsDone(); iter->Next()) {
// Operation on iter->Item()
// }
template <class T>
class ListIterator {
public:
ListIterator(List<T> *list) { current = list->first; }
// initialize an iterator
bool IsDone() { return current == NULL; };
// return TRUE if we are at the end of the list
T Item() { ASSERT(!IsDone()); return current->item; };
// return current element on list
void Next() { current = current->next; };
// update iterator to point to next
private:
ListElement<T> *current; // where we are in the list
};
#include "list.cc" // templates are really like macros
// so needs to be included in every
// file that uses the template
#endif // LIST_H

571
code/lib/sysdep.cc Executable file
View File

@@ -0,0 +1,571 @@
// sysdep.cc
// Implementation of system-dependent interface. Nachos uses the
// routines defined here, rather than directly calling the UNIX library,
// to simplify porting between versions of UNIX, and even to
// other systems, such as MSDOS.
//
// On UNIX, almost all of these routines are simple wrappers
// for the underlying UNIX system calls.
//
// NOTE: all of these routines refer to operations on the underlying
// host machine (e.g., the DECstation, SPARC, etc.), supporting the
// Nachos simulation code. Nachos implements similar operations,
// (such as opening a file), but those are implemented in terms
// of hardware devices, which are simulated by calls to the underlying
// routines in the host workstation OS.
//
// This file includes lots of calls to C routines. C++ requires
// us to wrap all C definitions with a "extern "C" block".
// This prevents the internal forms of the names from being
// changed by the C++ compiler.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "debug.h"
#include "sysdep.h"
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <cerrno>
#ifdef SOLARIS
// KMS
// for open()
#include <fcntl.h>
#endif
#ifdef LINUX // at this point, linux doesn't support mprotect
#define NO_MPROT
#endif
#ifdef DOS // neither does DOS
#define NO_MPROT
#endif
extern "C" {
#include <signal.h>
#include <sys/types.h>
#ifndef NO_MPROT
#include <sys/mman.h>
#endif
// UNIX routines called by procedures in this file
#if defined CYGWIN
size_t getpagesize(void);
#else
int getpagesize(void);
#endif
unsigned sleep(unsigned);
//#ifdef SOLARIS
//int usleep(useconds_t);
//#else
//void usleep(unsigned int); // rcgood - to avoid spinning processes.
//#endif
#ifndef NO_MPROT
#ifdef OSF
#define OSF_OR_AIX
#endif
#ifdef AIX
#define OSF_OR_AIX
#endif
#ifdef OSF_OR_AIX
int mprotect(const void *, long unsigned int, int);
#else
int mprotect(char *, unsigned int, int);
#endif
#endif
#if defined(BSD) || defined(SOLARIS) || defined(LINUX)
//KMS
// added Solaris and LINUX
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
#else
int select(int numBits, void *readFds, void *writeFds, void *exceptFds,
struct timeval *timeout);
#endif
int socket(int, int, int);
#if defined(SUNOS) || defined(ULTRIX)
long tell(int);
int bind (int, const void*, int);
int recvfrom (int, void*, int, int, void*, int *);
int sendto (int, const void*, int, int, void*, int);
#endif
}
//----------------------------------------------------------------------
// CallOnUserAbort
// Arrange that "func" will be called when the user aborts (e.g., by
// hitting ctl-C.
//----------------------------------------------------------------------
void
CallOnUserAbort(void (*func)(int))
{
(void)signal(SIGINT, func);
}
//----------------------------------------------------------------------
// Delay
// Put the UNIX process running Nachos to sleep for x seconds,
// to give the user time to start up another invocation of Nachos
// in a different UNIX shell.
//----------------------------------------------------------------------
void
Delay(int seconds)
{
(void) sleep((unsigned) seconds);
}
//----------------------------------------------------------------------
// UDelay
// Put the UNIX process running Nachos to sleep for x microseconds,
// to prevent an idle Nachos process from spinning...
//----------------------------------------------------------------------
void
UDelay(unsigned int useconds)
{
//#ifdef SOLARIS
// usleep(useconds_t useconds);
//#else
// usleep(useconds);
//#endif /* SOLARIS */
}
//----------------------------------------------------------------------
// Abort
// Quit and drop core.
//----------------------------------------------------------------------
void
Abort()
{
abort();
}
//----------------------------------------------------------------------
// Exit
// Quit without dropping core.
//----------------------------------------------------------------------
void
Exit(int exitCode)
{
exit(exitCode);
}
//----------------------------------------------------------------------
// RandomInit
// Initialize the pseudo-random number generator. We use the
// now obsolete "srand" and "rand" because they are more portable!
//----------------------------------------------------------------------
void
RandomInit(unsigned seed)
{
srand(seed);
}
//----------------------------------------------------------------------
// RandomNumber
// Return a pseudo-random number.
//----------------------------------------------------------------------
unsigned int
RandomNumber()
{
return rand();
}
//----------------------------------------------------------------------
// AllocBoundedArray
// Return an array, with the two pages just before
// and after the array unmapped, to catch illegal references off
// the end of the array. Particularly useful for catching overflow
// beyond fixed-size thread execution stacks.
//
// Note: Just return the useful part!
//
// "size" -- amount of useful space needed (in bytes)
//----------------------------------------------------------------------
char *
AllocBoundedArray(int size)
{
#ifdef NO_MPROT
return new char[size];
#else
int pgSize = getpagesize();
char *ptr = new char[pgSize * 2 + size];
mprotect(ptr, pgSize, 0);
mprotect(ptr + pgSize + size, pgSize, 0);
return ptr + pgSize;
#endif
}
//----------------------------------------------------------------------
// DeallocBoundedArray
// Deallocate an array of integers, unprotecting its two boundary pages.
//
// "ptr" -- the array to be deallocated
// "size" -- amount of useful space in the array (in bytes)
//----------------------------------------------------------------------
#ifdef NO_MPROT
void
DeallocBoundedArray(char *ptr, int /* size */)
{
delete [] ptr;
}
#else
void
DeallocBoundedArray(char *ptr, int size)
{
int pgSize = getpagesize();
mprotect(ptr - pgSize, pgSize, PROT_READ | PROT_WRITE | PROT_EXEC);
mprotect(ptr + size, pgSize, PROT_READ | PROT_WRITE | PROT_EXEC);
delete [] (ptr - pgSize);
}
#endif
//----------------------------------------------------------------------
// PollFile
// Check open file or open socket to see if there are any
// characters that can be read immediately. If so, read them
// in, and return TRUE.
//
// "fd" -- the file descriptor of the file to be polled
//----------------------------------------------------------------------
bool
PollFile(int fd)
{
#if defined(SOLARIS) || defined(LINUX)
// KMS
fd_set rfd,wfd,xfd;
#else
int rfd = (1 << fd), wfd = 0, xfd = 0;
#endif
int retVal;
struct timeval pollTime;
#if defined(SOLARIS) || defined(LINUX)
// KMS
FD_ZERO(&rfd);
FD_ZERO(&wfd);
FD_ZERO(&xfd);
FD_SET(fd,&rfd);
#endif
// don't wait if there are no characters on the file
pollTime.tv_sec = 0;
pollTime.tv_usec = 0;
// poll file or socket
#if defined(BSD)
retVal = select(32, (fd_set*)&rfd, (fd_set*)&wfd, (fd_set*)&xfd, &pollTime);
#elif defined(SOLARIS) || defined(LINUX)
// KMS
retVal = select(32, &rfd, &wfd, &xfd, &pollTime);
#else
retVal = select(32, &rfd, &wfd, &xfd, &pollTime);
#endif
ASSERT((retVal == 0) || (retVal == 1));
if (retVal == 0)
return FALSE; // no char waiting to be read
return TRUE;
}
//----------------------------------------------------------------------
// OpenForWrite
// Open a file for writing. Create it if it doesn't exist; truncate it
// if it does already exist. Return the file descriptor.
//
// "name" -- file name
//----------------------------------------------------------------------
int
OpenForWrite(char *name)
{
int fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0666);
ASSERT(fd >= 0);
return fd;
}
//----------------------------------------------------------------------
// OpenForReadWrite
// Open a file for reading or writing.
// Return the file descriptor, or error if it doesn't exist.
//
// "name" -- file name
//----------------------------------------------------------------------
int
OpenForReadWrite(char *name, bool crashOnError)
{
int fd = open(name, O_RDWR, 0);
ASSERT(!crashOnError || fd >= 0);
return fd;
}
//----------------------------------------------------------------------
// Read
// Read characters from an open file. Abort if read fails.
//----------------------------------------------------------------------
void
Read(int fd, char *buffer, int nBytes)
{
int retVal = read(fd, buffer, nBytes);
ASSERT(retVal == nBytes);
}
//----------------------------------------------------------------------
// ReadPartial
// Read characters from an open file, returning as many as are
// available.
//----------------------------------------------------------------------
int
ReadPartial(int fd, char *buffer, int nBytes)
{
return read(fd, buffer, nBytes);
}
//----------------------------------------------------------------------
// WriteFile
// Write characters to an open file. Abort if write fails.
//----------------------------------------------------------------------
void
WriteFile(int fd, char *buffer, int nBytes)
{
int retVal = write(fd, buffer, nBytes);
ASSERT(retVal == nBytes);
}
//----------------------------------------------------------------------
// Lseek
// Change the location within an open file. Abort on error.
//----------------------------------------------------------------------
void
Lseek(int fd, int offset, int whence)
{
int retVal = lseek(fd, offset, whence);
ASSERT(retVal >= 0);
}
//----------------------------------------------------------------------
// Tell
// Report the current location within an open file.
//----------------------------------------------------------------------
int
Tell(int fd)
{
#if defined(BSD) || defined(SOLARIS) || defined(LINUX)
return lseek(fd,0,SEEK_CUR); // 386BSD doesn't have the tell() system call
// neither do Solaris and Linux -KMS
#else
return tell(fd);
#endif
}
//----------------------------------------------------------------------
// Close
// Close a file. Abort on error.
//----------------------------------------------------------------------
int
Close(int fd)
{
int retVal = close(fd);
ASSERT(retVal >= 0);
return retVal;
}
//----------------------------------------------------------------------
// Unlink
// Delete a file.
//----------------------------------------------------------------------
bool
Unlink(char *name)
{
return unlink(name);
}
//----------------------------------------------------------------------
// OpenSocket
// Open an interprocess communication (IPC) connection. For now,
// just open a datagram port where other Nachos (simulating
// workstations on a network) can send messages to this Nachos.
//----------------------------------------------------------------------
int
OpenSocket()
{
int sockID;
sockID = socket(AF_UNIX, SOCK_DGRAM, 0);
ASSERT(sockID >= 0);
return sockID;
}
//----------------------------------------------------------------------
// CloseSocket
// Close the IPC connection.
//----------------------------------------------------------------------
void
CloseSocket(int sockID)
{
(void) close(sockID);
}
//----------------------------------------------------------------------
// InitSocketName
// Initialize a UNIX socket address -- magical!
//----------------------------------------------------------------------
static void
InitSocketName(struct sockaddr_un *uname, char *name)
{
uname->sun_family = AF_UNIX;
strcpy(uname->sun_path, name);
}
//----------------------------------------------------------------------
// AssignNameToSocket
// Give a UNIX file name to the IPC port, so other instances of Nachos
// can locate the port.
//----------------------------------------------------------------------
void
AssignNameToSocket(char *socketName, int sockID)
{
struct sockaddr_un uName;
int retVal;
(void) unlink(socketName); // in case it's still around from last time
InitSocketName(&uName, socketName);
retVal = bind(sockID, (struct sockaddr *) &uName, sizeof(uName));
ASSERT(retVal >= 0);
DEBUG(dbgNet, "Created socket " << socketName);
}
//----------------------------------------------------------------------
// DeAssignNameToSocket
// Delete the UNIX file name we assigned to our IPC port, on cleanup.
//----------------------------------------------------------------------
void
DeAssignNameToSocket(char *socketName)
{
(void) unlink(socketName);
}
//----------------------------------------------------------------------
// PollSocket
// Return TRUE if there are any messages waiting to arrive on the
// IPC port.
//----------------------------------------------------------------------
bool
PollSocket(int sockID)
{
return PollFile(sockID); // on UNIX, socket ID's are just file ID's
}
//----------------------------------------------------------------------
// ReadFromSocket
// Read a fixed size packet off the IPC port. Abort on error.
//----------------------------------------------------------------------
void
ReadFromSocket(int sockID, char *buffer, int packetSize)
{
int retVal;
struct sockaddr_un uName;
#ifdef LINUX
socklen_t size = sizeof(uName);
#else
int size = sizeof(uName);
#endif
retVal = recvfrom(sockID, buffer, packetSize, 0,
(struct sockaddr *) &uName, &size);
if (retVal != packetSize) {
perror("in recvfrom");
#if defined CYGWIN
cerr << "called with " << packetSize << ", got back " << retVal
<< ", and " << "\n";
#else
cerr << "called with " << packetSize << ", got back " << retVal
<< ", and " << "\n";
#endif
}
ASSERT(retVal == packetSize);
}
//----------------------------------------------------------------------
// modified by KMS to add retry...
// SendToSocket
// Transmit a fixed size packet to another Nachos' IPC port.
// Try 10 times with a one second delay between attempts.
// This is useful, e.g., to give the other socket a chance
// to get set up.
// Terminate if we still fail after 10 tries.
//----------------------------------------------------------------------
void
SendToSocket(int sockID, char *buffer, int packetSize, char *toName)
{
struct sockaddr_un uName;
int retVal;
int retryCount;
InitSocketName(&uName, toName);
for(retryCount=0;retryCount < 10;retryCount++) {
retVal = sendto(sockID, buffer, packetSize, 0,
(struct sockaddr *) &uName, sizeof(uName));
if (retVal == packetSize) return;
// if we did not succeed, we should see a negative
// return value indicating complete failure. If we
// don't, something fishy is going on...
ASSERT(retVal < 0);
// wait a second before trying again
Delay(1);
}
// At this point, we have failed many times
// The most common reason for this is that the target machine
// has halted and its socket no longer exists.
// We simply do nothing (drop the packet).
// This may mask other kinds of failures, but it is the
// right thing to do in the common case.
}

75
code/lib/sysdep.h Executable file
View File

@@ -0,0 +1,75 @@
// sysdep.h
// System-dependent interface. Nachos uses the routines defined
// here, rather than directly calling the UNIX library functions, to
// simplify porting between versions of UNIX, and even to
// other systems, such as MSDOS and the Macintosh.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef SYSDEP_H
#define SYSDEP_H
#include "copyright.h"
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
// Process control: abort, exit, and sleep
extern void Abort();
extern void Exit(int exitCode);
extern void Delay(int seconds);
extern void UDelay(unsigned int usec);// rcgood - to avoid spinners.
// Initialize system so that cleanUp routine is called when user hits ctl-C
extern void CallOnUserAbort(void (*cleanup)(int));
// Initialize the pseudo random number generator
extern void RandomInit(unsigned seed);
extern unsigned int RandomNumber();
// Allocate, de-allocate an array, such that de-referencing
// just beyond either end of the array will cause an error
extern char *AllocBoundedArray(int size);
extern void DeallocBoundedArray(char *p, int size);
// Check file to see if there are any characters to be read.
// If no characters in the file, return without waiting.
extern bool PollFile(int fd);
// File operations: open/read/write/lseek/close, and check for error
// For simulating the disk and the console devices.
extern int OpenForWrite(char *name);
extern int OpenForReadWrite(char *name, bool crashOnError);
extern void Read(int fd, char *buffer, int nBytes);
extern int ReadPartial(int fd, char *buffer, int nBytes);
extern void WriteFile(int fd, char *buffer, int nBytes);
extern void Lseek(int fd, int offset, int whence);
extern int Tell(int fd);
extern int Close(int fd);
extern bool Unlink(char *name);
// Other C library routines that are used by Nachos.
// These are assumed to be portable, so we don't include a wrapper.
extern "C" {
int atoi(const char *str);
double atof(const char *str);
int abs(int i);
void bcopy(const void *s1, void *s2, size_t n);
void bzero(void *s, size_t n);
}
// Interprocess communication operations, for simulating the network
extern int OpenSocket();
extern void CloseSocket(int sockID);
extern void AssignNameToSocket(char *socketName, int sockID);
extern void DeAssignNameToSocket(char *socketName);
extern bool PollSocket(int sockID);
extern void ReadFromSocket(int sockID, char *buffer, int packetSize);
extern void SendToSocket(int sockID, char *buffer, int packetSize,char *toName);
#endif // SYSDEP_H

38
code/lib/utility.h Executable file
View File

@@ -0,0 +1,38 @@
// utility.h
// Miscellaneous useful definitions.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef UTILITY_H
#define UTILITY_H
#include "copyright.h"
// Miscellaneous useful routines
#define NULL 0
#define TRUE true
#define FALSE false
// #define bool int // necessary on the Mac?
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
// Divide and either round up or down
#define divRoundDown(n,s) ((n) / (s))
#define divRoundUp(n,s) (((n) / (s)) + ((((n) % (s)) > 0) ? 1 : 0))
// This declares the type "VoidFunctionPtr" to be a "pointer to a
// function taking an arbitrary pointer argument and returning nothing". With
// such a function pointer (say it is "func"), we can call it like this:
//
// (*func) ("help!");
//
// This is used by Thread::Fork as well as a couple of other places.
typedef void (*VoidFunctionPtr)(void *arg);
typedef void (*VoidNoArgFunctionPtr)();
#endif // UTILITY_H

44
code/machine/callback.h Executable file
View File

@@ -0,0 +1,44 @@
// callback.h
// Data structure to allow an object to register a "callback".
// On an asynchronous operation, the call to start the operation
// returns immediately. When the operation completes, the called
// object must somehow notify the caller of the completion.
// In the general case, the called object doesn't know the type
// of the caller.
//
// We implement this using virtual functions in C++. An object
// that needs to register a callback is set up as a derived class of
// the abstract base class "CallbackObj". When we pass a
// pointer to the object to a lower level module, that module
// calls back via "obj->CallBack()", without knowing the
// type of the object being called back.
//
// Note that this isn't a general-purpose mechanism,
// because a class can only register a single callback.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
//
#ifndef CALLBACK_H
#define CALLBACK_H
#include "copyright.h"
// Abstract base class for objects that register callbacks
class CallBackObj {
public:
virtual void CallBack() = 0;
protected:
CallBackObj() {}; // to prevent anyone from creating
// an instance of this class. Only
// allow creation of instances of
// classes derived from this class.
virtual ~CallBackObj() {};
};
#endif

174
code/machine/console.cc Executable file
View File

@@ -0,0 +1,174 @@
// console.cc
// Routines to simulate a serial port to a console device.
// A console has input (a keyboard) and output (a display).
// These are each simulated by operations on UNIX files.
// The simulated device is asynchronous, so we have to invoke
// the interrupt handler (after a simulated delay), to signal that
// a byte has arrived and/or that a written byte has departed.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "console.h"
#include "main.h"
#include "stdio.h"
//----------------------------------------------------------------------
// ConsoleInput::ConsoleInput
// Initialize the simulation of the input for a hardware console device.
//
// "readFile" -- UNIX file simulating the keyboard (NULL -> use stdin)
// "toCall" is the interrupt handler to call when a character arrives
// from the keyboard
//----------------------------------------------------------------------
ConsoleInput::ConsoleInput(char *readFile, CallBackObj *toCall)
{
if (readFile == NULL)
readFileNo = 0; // keyboard = stdin
else
readFileNo = OpenForReadWrite(readFile, TRUE); // should be read-only
// set up the stuff to emulate asynchronous interrupts
callWhenAvail = toCall;
incoming = EOF;
// start polling for incoming keystrokes
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleReadInt);
}
//----------------------------------------------------------------------
// ConsoleInput::~ConsoleInput
// Clean up console input emulation
//----------------------------------------------------------------------
ConsoleInput::~ConsoleInput()
{
if (readFileNo != 0)
Close(readFileNo);
}
//----------------------------------------------------------------------
// ConsoleInput::CallBack()
// Simulator calls this when a character may be available to be
// read in from the simulated keyboard (eg, the user typed something).
//
// First check to make sure character is available.
// Then invoke the "callBack" registered by whoever wants the character.
//----------------------------------------------------------------------
void
ConsoleInput::CallBack()
{
char c;
int readCount;
ASSERT(incoming == EOF);
if (!PollFile(readFileNo)) { // nothing to be read
// schedule the next time to poll for a packet
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleReadInt);
} else {
// otherwise, try to read a character
readCount = ReadPartial(readFileNo, &c, sizeof(char));
if (readCount == 0) {
// this seems to happen at end of file, when the
// console input is a regular file
// don't schedule an interrupt, since there will never
// be any more input
// just do nothing....
}
else {
// save the character and notify the OS that
// it is available
ASSERT(readCount == sizeof(char));
incoming = c;
kernel->stats->numConsoleCharsRead++;
}
callWhenAvail->CallBack();
}
}
//----------------------------------------------------------------------
// ConsoleInput::GetChar()
// Read a character from the input buffer, if there is any there.
// Either return the character, or EOF if none buffered.
//----------------------------------------------------------------------
char
ConsoleInput::GetChar()
{
char ch = incoming;
if (incoming != EOF) { // schedule when next char will arrive
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleReadInt);
}
incoming = EOF;
return ch;
}
//----------------------------------------------------------------------
// ConsoleOutput::ConsoleOutput
// Initialize the simulation of the output for a hardware console device.
//
// "writeFile" -- UNIX file simulating the display (NULL -> use stdout)
// "toCall" is the interrupt handler to call when a write to
// the display completes.
//----------------------------------------------------------------------
ConsoleOutput::ConsoleOutput(char *writeFile, CallBackObj *toCall)
{
if (writeFile == NULL)
writeFileNo = 1; // display = stdout
else
writeFileNo = OpenForWrite(writeFile);
callWhenDone = toCall;
putBusy = FALSE;
}
//----------------------------------------------------------------------
// ConsoleOutput::~ConsoleOutput
// Clean up console output emulation
//----------------------------------------------------------------------
ConsoleOutput::~ConsoleOutput()
{
if (writeFileNo != 1)
Close(writeFileNo);
}
//----------------------------------------------------------------------
// ConsoleOutput::CallBack()
// Simulator calls this when the next character can be output to the
// display.
//----------------------------------------------------------------------
void
ConsoleOutput::CallBack()
{
putBusy = FALSE;
kernel->stats->numConsoleCharsWritten++;
callWhenDone->CallBack();
}
//----------------------------------------------------------------------
// ConsoleOutput::PutChar()
// Write a character to the simulated display, schedule an interrupt
// to occur in the future, and return.
//----------------------------------------------------------------------
void
ConsoleOutput::PutChar(char ch)
{
ASSERT(putBusy == FALSE);
WriteFile(writeFileNo, &ch, sizeof(char));
putBusy = TRUE;
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleWriteInt);
}

90
code/machine/console.h Executable file
View File

@@ -0,0 +1,90 @@
// console.h
// Data structures to simulate the behavior of a terminal
// I/O device. A terminal has two parts -- a keyboard input,
// and a display output, each of which produces/accepts
// characters sequentially.
//
// The console hardware device is asynchronous. When a character is
// written to the device, the routine returns immediately, and an
// interrupt handler is called later when the I/O completes.
// For reads, an interrupt handler is called when a character arrives.
//
// In either case, the serial line connecting the computer
// to the console has limited bandwidth (like a modem!), and so
// each character takes measurable time.
//
// The user of the device registers itself to be called "back" when
// the read/write interrupts occur. There is a separate interrupt
// for read and write, and the device is "duplex" -- a character
// can be outgoing and incoming at the same time.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef CONSOLE_H
#define CONSOLE_H
#include "copyright.h"
#include "utility.h"
#include "callback.h"
// The following two classes define the input (and output) side of a
// hardware console device. Input (and output) to the device is simulated
// by reading (and writing) to the UNIX file "readFile" (and "writeFile").
//
// Since input (and output) to the device is asynchronous, the interrupt
// handler "callWhenAvail" is called when a character has arrived to be
// read in (and "callWhenDone" is called when an output character has been
// "put" so that the next character can be written).
//
// In practice, usually a single hardware thing that does both
// serial input and serial output. But conceptually simpler to
// use two objects.
class ConsoleInput : public CallBackObj {
public:
ConsoleInput(char *readFile, CallBackObj *toCall);
// initialize hardware console input
~ConsoleInput(); // clean up console emulation
char GetChar(); // Poll the console input. If a char is
// available, return it. Otherwise, return EOF.
// "callWhenAvail" is called whenever there is
// a char to be gotten
void CallBack(); // Invoked when a character arrives
// from the keyboard.
private:
int readFileNo; // UNIX file emulating the keyboard
CallBackObj *callWhenAvail; // Interrupt handler to call when
// there is a char to be read
char incoming; // Contains the character to be read,
// if there is one available.
// Otherwise contains EOF.
};
class ConsoleOutput : public CallBackObj {
public:
ConsoleOutput(char *writeFile, CallBackObj *toCall);
// initialize hardware console output
~ConsoleOutput(); // clean up console emulation
void PutChar(char ch); // Write "ch" to the console display,
// and return immediately. "callWhenDone"
// will called when the I/O completes.
void CallBack(); // Invoked when next character can be put
// out to the display.
private:
int writeFileNo; // UNIX file emulating the display
CallBackObj *callWhenDone; // Interrupt handler to call when
// the next char can be put
bool putBusy; // Is a PutChar operation in progress?
// If so, you can't do another one!
};
#endif // CONSOLE_H

268
code/machine/disk.cc Executable file
View File

@@ -0,0 +1,268 @@
// disk.cc
// Routines to simulate a physical disk device; reading and writing
// to the disk is simulated as reading and writing to a UNIX file.
// See disk.h for details about the behavior of disks (and
// therefore about the behavior of this simulation).
//
// Disk operations are asynchronous, so we have to invoke an interrupt
// handler when the simulated operation completes.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "disk.h"
#include "debug.h"
#include "sysdep.h"
#include "main.h"
// We put a magic number at the front of the UNIX file representing the
// disk, to make it less likely we will accidentally treat a useful file
// as a disk (which would probably trash the file's contents).
const int MagicNumber = 0x456789ab;
const int MagicSize = sizeof(int);
const int DiskSize = (MagicSize + (NumSectors * SectorSize));
//----------------------------------------------------------------------
// Disk::Disk()
// Initialize a simulated disk. Open the UNIX file (creating it
// if it doesn't exist), and check the magic number to make sure it's
// ok to treat it as Nachos disk storage.
//
// "toCall" -- object to call when disk read/write request completes
//----------------------------------------------------------------------
Disk::Disk(CallBackObj *toCall)
{
int magicNum;
int tmp = 0;
DEBUG(dbgDisk, "Initializing the disk.");
callWhenDone = toCall;
lastSector = 0;
bufferInit = 0;
sprintf(diskname,"DISK_%d",kernel->hostName);
fileno = OpenForReadWrite(diskname, FALSE);
if (fileno >= 0) { // file exists, check magic number
Read(fileno, (char *) &magicNum, MagicSize);
ASSERT(magicNum == MagicNumber);
} else { // file doesn't exist, create it
fileno = OpenForWrite(diskname);
magicNum = MagicNumber;
WriteFile(fileno, (char *) &magicNum, MagicSize); // write magic number
// need to write at end of file, so that reads will not return EOF
Lseek(fileno, DiskSize - sizeof(int), 0);
WriteFile(fileno, (char *)&tmp, sizeof(int));
}
active = FALSE;
}
//----------------------------------------------------------------------
// Disk::~Disk()
// Clean up disk simulation, by closing the UNIX file representing the
// disk.
//----------------------------------------------------------------------
Disk::~Disk()
{
Close(fileno);
}
//----------------------------------------------------------------------
// Disk::PrintSector()
// Dump the data in a disk read/write request, for debugging.
//----------------------------------------------------------------------
static void
PrintSector (bool writing, int sector, char *data)
{
int *p = (int *) data;
if (writing)
cout << "Writing sector: " << sector << "\n";
else
cout << "Reading sector: " << sector << "\n";
for (unsigned int i = 0; i < (SectorSize/sizeof(int)); i++) {
cout << p[i] << " ";
}
cout << "\n";
}
//----------------------------------------------------------------------
// Disk::ReadRequest/WriteRequest
// Simulate a request to read/write a single disk sector
// Do the read/write immediately to the UNIX file
// Set up an interrupt handler to be called later,
// that will notify the caller when the simulator says
// the operation has completed.
//
// Note that a disk only allows an entire sector to be read/written,
// not part of a sector.
//
// "sectorNumber" -- the disk sector to read/write
// "data" -- the bytes to be written, the buffer to hold the incoming bytes
//----------------------------------------------------------------------
void
Disk::ReadRequest(int sectorNumber, char* data)
{
int ticks = ComputeLatency(sectorNumber, FALSE);
ASSERT(!active); // only one request at a time
ASSERT((sectorNumber >= 0) && (sectorNumber < NumSectors));
DEBUG(dbgDisk, "Reading from sector " << sectorNumber);
Lseek(fileno, SectorSize * sectorNumber + MagicSize, 0);
Read(fileno, data, SectorSize);
if (debug->IsEnabled('d'))
PrintSector(FALSE, sectorNumber, data);
active = TRUE;
UpdateLast(sectorNumber);
kernel->stats->numDiskReads++;
kernel->interrupt->Schedule(this, ticks, DiskInt);
}
void
Disk::WriteRequest(int sectorNumber, char* data)
{
int ticks = ComputeLatency(sectorNumber, TRUE);
ASSERT(!active);
ASSERT((sectorNumber >= 0) && (sectorNumber < NumSectors));
DEBUG(dbgDisk, "Writing to sector " << sectorNumber);
Lseek(fileno, SectorSize * sectorNumber + MagicSize, 0);
WriteFile(fileno, data, SectorSize);
if (debug->IsEnabled('d'))
PrintSector(TRUE, sectorNumber, data);
active = TRUE;
UpdateLast(sectorNumber);
kernel->stats->numDiskWrites++;
kernel->interrupt->Schedule(this, ticks, DiskInt);
}
//----------------------------------------------------------------------
// Disk::CallBack()
// Called by the machine simulation when the disk interrupt occurs.
//----------------------------------------------------------------------
void
Disk::CallBack ()
{
active = FALSE;
callWhenDone->CallBack();
}
//----------------------------------------------------------------------
// Disk::TimeToSeek()
// Returns how long it will take to position the disk head over the correct
// track on the disk. Since when we finish seeking, we are likely
// to be in the middle of a sector that is rotating past the head,
// we also return how long until the head is at the next sector boundary.
//
// Disk seeks at one track per SeekTime ticks (cf. stats.h)
// and rotates at one sector per RotationTime ticks
//----------------------------------------------------------------------
int
Disk::TimeToSeek(int newSector, int *rotation)
{
int newTrack = newSector / SectorsPerTrack;
int oldTrack = lastSector / SectorsPerTrack;
int seek = abs(newTrack - oldTrack) * SeekTime;
// how long will seek take?
int over = (kernel->stats->totalTicks + seek) % RotationTime;
// will we be in the middle of a sector when
// we finish the seek?
*rotation = 0;
if (over > 0) // if so, need to round up to next full sector
*rotation = RotationTime - over;
return seek;
}
//----------------------------------------------------------------------
// Disk::ModuloDiff()
// Return number of sectors of rotational delay between target sector
// "to" and current sector position "from"
//----------------------------------------------------------------------
int
Disk::ModuloDiff(int to, int from)
{
int toOffset = to % SectorsPerTrack;
int fromOffset = from % SectorsPerTrack;
return ((toOffset - fromOffset) + SectorsPerTrack) % SectorsPerTrack;
}
//----------------------------------------------------------------------
// Disk::ComputeLatency()
// Return how long will it take to read/write a disk sector, from
// the current position of the disk head.
//
// Latency = seek time + rotational latency + transfer time
// Disk seeks at one track per SeekTime ticks (cf. stats.h)
// and rotates at one sector per RotationTime ticks
//
// To find the rotational latency, we first must figure out where the
// disk head will be after the seek (if any). We then figure out
// how long it will take to rotate completely past newSector after
// that point.
//
// The disk also has a "track buffer"; the disk continuously reads
// the contents of the current disk track into the buffer. This allows
// read requests to the current track to be satisfied more quickly.
// The contents of the track buffer are discarded after every seek to
// a new track.
//----------------------------------------------------------------------
int
Disk::ComputeLatency(int newSector, bool writing)
{
int rotation;
int seek = TimeToSeek(newSector, &rotation);
int timeAfter = kernel->stats->totalTicks + seek + rotation;
#ifndef NOTRACKBUF // turn this on if you don't want the track buffer stuff
// check if track buffer applies
if ((writing == FALSE) && (seek == 0)
&& (((timeAfter - bufferInit) / RotationTime)
> ModuloDiff(newSector, bufferInit / RotationTime))) {
DEBUG(dbgDisk, "Request latency = " << RotationTime);
return RotationTime; // time to transfer sector from the track buffer
}
#endif
rotation += ModuloDiff(newSector, timeAfter / RotationTime) * RotationTime;
DEBUG(dbgDisk, "Request latency = " << (seek + rotation + RotationTime));
return(seek + rotation + RotationTime);
}
//----------------------------------------------------------------------
// Disk::UpdateLast
// Keep track of the most recently requested sector. So we can know
// what is in the track buffer.
//----------------------------------------------------------------------
void
Disk::UpdateLast(int newSector)
{
int rotate;
int seek = TimeToSeek(newSector, &rotate);
if (seek != 0)
bufferInit = kernel->stats->totalTicks + seek + rotate;
lastSector = newSector;
DEBUG(dbgDisk, "Updating last sector = " << lastSector << " , " << bufferInit);
}

92
code/machine/disk.h Executable file
View File

@@ -0,0 +1,92 @@
// disk.h
// Data structures to emulate a physical disk. A physical disk
// can accept (one at a time) requests to read/write a disk sector;
// when the request is satisfied, the CPU gets an interrupt, and
// the next request can be sent to the disk.
//
// Disk contents are preserved across machine crashes, but if
// a file system operation (eg, create a file) is in progress when the
// system shuts down, the file system may be corrupted.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef DISK_H
#define DISK_H
#include "copyright.h"
#include "utility.h"
#include "callback.h"
// The following class defines a physical disk I/O device. The disk
// has a single surface, split up into "tracks", and each track split
// up into "sectors" (the same number of sectors on each track, and each
// sector has the same number of bytes of storage).
//
// Addressing is by sector number -- each sector on the disk is given
// a unique number: track * SectorsPerTrack + offset within a track.
//
// As with other I/O devices, the raw physical disk is an asynchronous device --
// requests to read or write portions of the disk return immediately,
// and an interrupt is invoked later to signal that the operation completed.
//
// The physical disk is in fact simulated via operations on a UNIX file.
//
// To make life a little more realistic, the simulated time for
// each operation reflects a "track buffer" -- RAM to store the contents
// of the current track as the disk head passes by. The idea is that the
// disk always transfers to the track buffer, in case that data is requested
// later on. This has the benefit of eliminating the need for
// "skip-sector" scheduling -- a read request which comes in shortly after
// the head has passed the beginning of the sector can be satisfied more
// quickly, because its contents are in the track buffer. Most
// disks these days now come with a track buffer.
//
// The track buffer simulation can be disabled by compiling with -DNOTRACKBUF
const int SectorSize = 128; // number of bytes per disk sector
const int SectorsPerTrack = 32; // number of sectors per disk track
const int NumTracks = 32; // number of tracks per disk
const int NumSectors = (SectorsPerTrack * NumTracks);
// total # of sectors per disk
class Disk : public CallBackObj {
public:
Disk(CallBackObj *toCall); // Create a simulated disk.
// Invoke toCall->CallBack()
// when each request completes.
~Disk(); // Deallocate the disk.
void ReadRequest(int sectorNumber, char* data);
// Read/write an single disk sector.
// These routines send a request to
// the disk and return immediately.
// Only one request allowed at a time!
void WriteRequest(int sectorNumber, char* data);
void CallBack(); // Invoked when disk request
// finishes. In turn calls, callWhenDone.
int ComputeLatency(int newSector, bool writing);
// Return how long a request to
// newSector will take:
// (seek + rotational delay + transfer)
private:
int fileno; // UNIX file number for simulated disk
char diskname[32]; // name of simulated disk's file
CallBackObj *callWhenDone; // Invoke when any disk request finishes
bool active; // Is a disk operation in progress?
int lastSector; // The previous disk request
int bufferInit; // When the track buffer started
// being loaded
int TimeToSeek(int newSector, int *rotate); // time to get to the new track
int ModuloDiff(int to, int from); // # sectors between to and from
void UpdateLast(int newSector);
};
#endif // DISK_H

361
code/machine/interrupt.cc Executable file
View File

@@ -0,0 +1,361 @@
// interrupt.cc
// Routines to simulate hardware interrupts.
//
// The hardware provides a routine (SetLevel) to enable or disable
// interrupts.
//
// In order to emulate the hardware, we need to keep track of all
// interrupts the hardware devices would cause, and when they
// are supposed to occur.
//
// This module also keeps track of simulated time. Time advances
// only when the following occur:
// interrupts are re-enabled
// a user instruction is executed
// there is nothing in the ready queue
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "interrupt.h"
#include "main.h"
// String definitions for debugging messages
static char *intLevelNames[] = { "off", "on"};
static char *intTypeNames[] = { "timer", "disk", "console write",
"console read", "network send",
"network recv"};
//----------------------------------------------------------------------
// PendingInterrupt::PendingInterrupt
// Initialize a hardware device interrupt that is to be scheduled
// to occur in the near future.
//
// "callOnInt" is the object to call when the interrupt occurs
// "time" is when (in simulated time) the interrupt is to occur
// "kind" is the hardware device that generated the interrupt
//----------------------------------------------------------------------
PendingInterrupt::PendingInterrupt(CallBackObj *callOnInt,
int time, IntType kind)
{
callOnInterrupt = callOnInt;
when = time;
type = kind;
}
//----------------------------------------------------------------------
// PendingCompare
// Compare to interrupts based on which should occur first.
//----------------------------------------------------------------------
static int
PendingCompare (PendingInterrupt *x, PendingInterrupt *y)
{
if (x->when < y->when) { return -1; }
else if (x->when > y->when) { return 1; }
else { return 0; }
}
//----------------------------------------------------------------------
// Interrupt::Interrupt
// Initialize the simulation of hardware device interrupts.
//
// Interrupts start disabled, with no interrupts pending, etc.
//----------------------------------------------------------------------
Interrupt::Interrupt()
{
level = IntOff;
pending = new SortedList<PendingInterrupt *>(PendingCompare);
inHandler = FALSE;
yieldOnReturn = FALSE;
status = SystemMode;
}
//----------------------------------------------------------------------
// Interrupt::~Interrupt
// De-allocate the data structures needed by the interrupt simulation.
//----------------------------------------------------------------------
Interrupt::~Interrupt()
{
while (!pending->IsEmpty()) {
delete pending->RemoveFront();
}
delete pending;
}
//----------------------------------------------------------------------
// Interrupt::ChangeLevel
// Change interrupts to be enabled or disabled, without advancing
// the simulated time (normally, enabling interrupts advances the time).
//
// Used internally.
//
// "old" -- the old interrupt status
// "now" -- the new interrupt status
//----------------------------------------------------------------------
void
Interrupt::ChangeLevel(IntStatus old, IntStatus now)
{
level = now;
DEBUG(dbgInt, "\tinterrupts: " << intLevelNames[old] << " -> " << intLevelNames[now]);
}
//----------------------------------------------------------------------
// Interrupt::SetLevel
// Change interrupts to be enabled or disabled, and if interrupts
// are being enabled, advance simulated time by calling OneTick().
//
// Returns:
// The old interrupt status.
// Parameters:
// "now" -- the new interrupt status
//----------------------------------------------------------------------
IntStatus
Interrupt::SetLevel(IntStatus now)
{
IntStatus old = level;
// interrupt handlers are prohibited from enabling interrupts
ASSERT((now == IntOff) || (inHandler == FALSE));
ChangeLevel(old, now); // change to new state
if ((now == IntOn) && (old == IntOff)) {
OneTick(); // advance simulated time
}
return old;
}
//----------------------------------------------------------------------
// Interrupt::OneTick
// Advance simulated time and check if there are any pending
// interrupts to be called.
//
// Two things can cause OneTick to be called:
// interrupts are re-enabled
// a user instruction is executed
//----------------------------------------------------------------------
void
Interrupt::OneTick()
{
MachineStatus oldStatus = status;
Statistics *stats = kernel->stats;
// advance simulated time
if (status == SystemMode) {
stats->totalTicks += SystemTick;
stats->systemTicks += SystemTick;
} else {
stats->totalTicks += UserTick;
stats->userTicks += UserTick;
}
DEBUG(dbgInt, "== Tick " << stats->totalTicks << " ==");
// check any pending interrupts are now ready to fire
ChangeLevel(IntOn, IntOff); // first, turn off interrupts
// (interrupt handlers run with
// interrupts disabled)
CheckIfDue(FALSE); // check for pending interrupts
ChangeLevel(IntOff, IntOn); // re-enable interrupts
if (yieldOnReturn) { // if the timer device handler asked
// for a context switch, ok to do it now
yieldOnReturn = FALSE;
status = SystemMode; // yield is a kernel routine
kernel->currentThread->Yield();
status = oldStatus;
}
}
//----------------------------------------------------------------------
// Interrupt::YieldOnReturn
// Called from within an interrupt handler, to cause a context switch
// (for example, on a time slice) in the interrupted thread,
// when the handler returns.
//
// We can't do the context switch here, because that would switch
// out the interrupt handler, and we want to switch out the
// interrupted thread.
//----------------------------------------------------------------------
void
Interrupt::YieldOnReturn()
{
ASSERT(inHandler == TRUE);
yieldOnReturn = TRUE;
}
//----------------------------------------------------------------------
// Interrupt::Idle
// Routine called when there is nothing in the ready queue.
//
// Since something has to be running in order to put a thread
// on the ready queue, the only thing to do is to advance
// simulated time until the next scheduled hardware interrupt.
//
// If there are no pending interrupts, stop. There's nothing
// more for us to do.
//----------------------------------------------------------------------
void
Interrupt::Idle()
{
DEBUG(dbgInt, "Machine idling; checking for interrupts.");
status = IdleMode;
if (CheckIfDue(TRUE)) { // check for any pending interrupts
status = SystemMode;
return; // return in case there's now
// a runnable thread
}
// if there are no pending interrupts, and nothing is on the ready
// queue, it is time to stop. If the console or the network is
// operating, there are *always* pending interrupts, so this code
// is not reached. Instead, the halt must be invoked by the user program.
DEBUG(dbgInt, "Machine idle. No interrupts to do.");
cout << "No threads ready or runnable, and no pending interrupts.\n";
cout << "Assuming the program completed.\n";
Halt();
}
//----------------------------------------------------------------------
// Interrupt::Halt
// Shut down Nachos cleanly, printing out performance statistics.
//----------------------------------------------------------------------
void
Interrupt::Halt()
{
cout << "Machine halting!\n\n";
cout << "This is halt\n";
kernel->stats->Print();
delete kernel; // Never returns.
}
int
Interrupt::CreateFile(char *filename)
{
return kernel->CreateFile(filename);
}
//----------------------------------------------------------------------
// Interrupt::Schedule
// Arrange for the CPU to be interrupted when simulated time
// reaches "now + when".
//
// Implementation: just put it on a sorted list.
//
// NOTE: the Nachos kernel should not call this routine directly.
// Instead, it is only called by the hardware device simulators.
//
// "toCall" is the object to call when the interrupt occurs
// "fromNow" is how far in the future (in simulated time) the
// interrupt is to occur
// "type" is the hardware device that generated the interrupt
//----------------------------------------------------------------------
void
Interrupt::Schedule(CallBackObj *toCall, int fromNow, IntType type)
{
int when = kernel->stats->totalTicks + fromNow;
PendingInterrupt *toOccur = new PendingInterrupt(toCall, when, type);
DEBUG(dbgInt, "Scheduling interrupt handler the " << intTypeNames[type] << " at time = " << when);
ASSERT(fromNow > 0);
pending->Insert(toOccur);
}
//----------------------------------------------------------------------
// Interrupt::CheckIfDue
// Check if any interrupts are scheduled to occur, and if so,
// fire them off.
//
// Returns:
// TRUE, if we fired off any interrupt handlers
// Params:
// "advanceClock" -- if TRUE, there is nothing in the ready queue,
// so we should simply advance the clock to when the next
// pending interrupt would occur (if any).
//----------------------------------------------------------------------
bool
Interrupt::CheckIfDue(bool advanceClock)
{
PendingInterrupt *next;
Statistics *stats = kernel->stats;
ASSERT(level == IntOff); // interrupts need to be disabled,
// to invoke an interrupt handler
if (debug->IsEnabled(dbgInt)) {
DumpState();
}
if (pending->IsEmpty()) { // no pending interrupts
return FALSE;
}
next = pending->Front();
if (next->when > stats->totalTicks) {
if (!advanceClock) { // not time yet
return FALSE;
}
else { // advance the clock to next interrupt
stats->idleTicks += (next->when - stats->totalTicks);
stats->totalTicks = next->when;
// UDelay(1000L); // rcgood - to stop nachos from spinning.
}
}
DEBUG(dbgInt, "Invoking interrupt handler for the ");
DEBUG(dbgInt, intTypeNames[next->type] << " at time " << next->when);
if (kernel->machine != NULL) {
kernel->machine->DelayedLoad(0, 0);
}
inHandler = TRUE;
do {
next = pending->RemoveFront(); // pull interrupt off list
next->callOnInterrupt->CallBack();// call the interrupt handler
delete next;
} while (!pending->IsEmpty()
&& (pending->Front()->when <= stats->totalTicks));
inHandler = FALSE;
return TRUE;
}
//----------------------------------------------------------------------
// PrintPending
// Print information about an interrupt that is scheduled to occur.
// When, where, why, etc.
//----------------------------------------------------------------------
static void
PrintPending (PendingInterrupt *pending)
{
cout << "Interrupt handler "<< intTypeNames[pending->type];
cout << ", scheduled at " << pending->when;
}
//----------------------------------------------------------------------
// DumpState
// Print the complete interrupt state - the status, and all interrupts
// that are scheduled to occur in the future.
//----------------------------------------------------------------------
void
Interrupt::DumpState()
{
cout << "Time: " << kernel->stats->totalTicks;
cout << ", interrupts " << intLevelNames[level] << "\n";
cout << "Pending interrupts:\n";
pending->Apply(PrintPending);
cout << "\nEnd of pending interrupts\n";
}

145
code/machine/interrupt.h Executable file
View File

@@ -0,0 +1,145 @@
// interrupt.h
// Data structures to emulate low-level interrupt hardware.
//
// The hardware provides a routine (SetLevel) to enable or disable
// interrupts.
//
// In order to emulate the hardware, we need to keep track of all
// interrupts the hardware devices would cause, and when they
// are supposed to occur.
//
// This module also keeps track of simulated time. Time advances
// only when the following occur:
// interrupts are re-enabled
// a user instruction is executed
// there is nothing in the ready queue
//
// As a result, unlike real hardware, interrupts (and thus time-slice
// context switches) cannot occur 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 to invoke an
// interrupt in the hardware simulation).
//
// NOTE: 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.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef INTERRUPT_H
#define INTERRUPT_H
#include "copyright.h"
#include "list.h"
#include "callback.h"
// Interrupts can be disabled (IntOff) or enabled (IntOn)
enum IntStatus { IntOff, IntOn };
// Nachos can be running kernel code (SystemMode), user code (UserMode),
// or there can be no runnable thread, because the ready list
// is empty (IdleMode).
enum MachineStatus {IdleMode, SystemMode, UserMode};
// IntType records which hardware device generated an interrupt.
// In Nachos, we support a hardware timer device, a disk, a console
// display and keyboard, and a network.
enum IntType { TimerInt, DiskInt, ConsoleWriteInt, ConsoleReadInt,
NetworkSendInt, NetworkRecvInt};
// The following class defines an interrupt that is scheduled
// to occur in the future. The internal data structures are
// left public to make it simpler to manipulate.
class PendingInterrupt {
public:
PendingInterrupt(CallBackObj *callOnInt, int time, IntType kind);
// initialize an interrupt that will
// occur in the future
CallBackObj *callOnInterrupt;// The object (in the hardware device
// emulator) to call when the interrupt occurs
int when; // When the interrupt is supposed to fire
IntType type; // for debugging
};
// The following class defines the data structures for the simulation
// of hardware interrupts. We record whether interrupts are enabled
// or disabled, and any hardware interrupts that are scheduled to occur
// in the future.
class Interrupt {
public:
Interrupt(); // initialize the interrupt simulation
~Interrupt(); // de-allocate data structures
IntStatus SetLevel(IntStatus level);
// Disable or enable interrupts
// and return previous setting.
void Enable() { (void) SetLevel(IntOn); }
// Enable interrupts.
IntStatus getLevel() {return level;}
// Return whether interrupts
// are enabled or disabled
void Idle(); // The ready queue is empty, roll
// simulated time forward until the
// next interrupt
void Halt(); // quit and print out stats
void PrintInt(int number);
int CreateFile(char *filename);
void YieldOnReturn(); // cause a context switch on return
// from an interrupt handler
MachineStatus getStatus() { return status; }
void setStatus(MachineStatus st) { status = st; }
// idle, kernel, user
void DumpState(); // Print interrupt state
// NOTE: the following are internal to the hardware simulation code.
// DO NOT call these directly. I should make them "private",
// but they need to be public since they are called by the
// hardware device simulators.
void Schedule(CallBackObj *callTo, int when, IntType type);
// Schedule an interrupt to occur
// at time "when". This is called
// by the hardware device simulators.
void OneTick(); // Advance simulated time
private:
IntStatus level; // are interrupts enabled or disabled?
SortedList<PendingInterrupt *> *pending;
// the list of interrupts scheduled
// to occur in the future
//int writeFileNo; //UNIX file emulating the display
bool inHandler; // TRUE if we are running an interrupt handler
//bool putBusy; // Is a PrintInt operation in progress
//If so, you cannoot do another one
bool yieldOnReturn; // TRUE if we are to context switch
// on return from the interrupt handler
MachineStatus status; // idle, kernel mode, user mode
// these functions are internal to the interrupt simulation code
bool CheckIfDue(bool advanceClock);
// Check if any interrupts are supposed
// to occur now, and if so, do them
void ChangeLevel(IntStatus old, // SetLevel, without advancing the
IntStatus now); // simulated time
};
#endif // INTERRRUPT_H

224
code/machine/machine.cc Executable file
View File

@@ -0,0 +1,224 @@
// machine.cc
// Routines for simulating the execution of user programs.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "machine.h"
#include "main.h"
// Textual names of the exceptions that can be generated by user program
// execution, for debugging.
static char* exceptionNames[] = { "no exception", "syscall",
"page fault/no TLB entry", "page read only",
"bus error", "address error", "overflow",
"illegal instruction" };
//----------------------------------------------------------------------
// CheckEndian
// Check to be sure that the host really uses the format it says it
// does, for storing the bytes of an integer. Stop on error.
//----------------------------------------------------------------------
static
void CheckEndian()
{
union checkit {
char charword[4];
unsigned int intword;
} check;
check.charword[0] = 1;
check.charword[1] = 2;
check.charword[2] = 3;
check.charword[3] = 4;
#ifdef HOST_IS_BIG_ENDIAN
ASSERT (check.intword == 0x01020304);
#else
ASSERT (check.intword == 0x04030201);
#endif
}
//----------------------------------------------------------------------
// Machine::Machine
// Initialize the simulation of user program execution.
//
// "debug" -- if TRUE, drop into the debugger after each user instruction
// is executed.
//----------------------------------------------------------------------
Machine::Machine(bool debug)
{
int i;
for (i = 0; i < NumTotalRegs; i++)
registers[i] = 0;
mainMemory = new char[MemorySize];
for (i = 0; i < MemorySize; i++)
mainMemory[i] = 0;
#ifdef USE_TLB
tlb = new TranslationEntry[TLBSize];
for (i = 0; i < TLBSize; i++)
tlb[i].valid = FALSE;
pageTable = NULL;
#else // use linear page table
tlb = NULL;
pageTable = NULL;
#endif
singleStep = debug;
CheckEndian();
}
//----------------------------------------------------------------------
// Machine::~Machine
// De-allocate the data structures used to simulate user program execution.
//----------------------------------------------------------------------
Machine::~Machine()
{
delete [] mainMemory;
if (tlb != NULL)
delete [] tlb;
}
//----------------------------------------------------------------------
// Machine::RaiseException
// Transfer control to the Nachos kernel from user mode, because
// the user program either invoked a system call, or some exception
// occured (such as the address translation failed).
//
// "which" -- the cause of the kernel trap
// "badVaddr" -- the virtual address causing the trap, if appropriate
//----------------------------------------------------------------------
void
Machine::RaiseException(ExceptionType which, int badVAddr)
{
DEBUG(dbgMach, "Exception: " << exceptionNames[which]);
registers[BadVAddrReg] = badVAddr;
DelayedLoad(0, 0); // finish anything in progress
kernel->interrupt->setStatus(SystemMode);
ExceptionHandler(which); // interrupts are enabled at this point
kernel->interrupt->setStatus(UserMode);
}
//----------------------------------------------------------------------
// Machine::Debugger
// Primitive debugger for user programs. Note that we can't use
// gdb to debug user programs, since gdb doesn't run on top of Nachos.
// It could, but you'd have to implement *a lot* more system calls
// to get it to work!
//
// So just allow single-stepping, and printing the contents of memory.
//----------------------------------------------------------------------
void Machine::Debugger()
{
char *buf = new char[80];
int num;
bool done = FALSE;
kernel->interrupt->DumpState();
DumpState();
while (!done) {
// read commands until we should proceed with more execution
// prompt for input, giving current simulation time in the prompt
cout << kernel->stats->totalTicks << ">";
// read one line of input (80 chars max)
cin.get(buf, 80);
if (sscanf(buf, "%d", &num) == 1) {
runUntilTime = num;
done = TRUE;
}
else {
runUntilTime = 0;
switch (*buf) {
case '\0':
done = TRUE;
break;
case 'c':
singleStep = FALSE;
done = TRUE;
break;
case '?':
cout << "Machine commands:\n";
cout << " <return> execute one instruction\n";
cout << " <number> run until the given timer tick\n";
cout << " c run until completion\n";
cout << " ? print help message\n";
break;
default:
cout << "Unknown command: " << buf << "\n";
cout << "Type ? for help.\n";
}
}
// consume the newline delimiter, which does not get
// eaten by cin.get(buf,80) above.
buf[0] = cin.get();
}
delete [] buf;
}
//----------------------------------------------------------------------
// Machine::DumpState
// Print the user program's CPU state. We might print the contents
// of memory, but that seemed like overkill.
//----------------------------------------------------------------------
void
Machine::DumpState()
{
int i;
cout << "Machine registers:\n";
for (i = 0; i < NumGPRegs; i++) {
switch (i) {
case StackReg:
cout << "\tSP(" << i << "):\t" << registers[i];
break;
case RetAddrReg:
cout << "\tRA(" << i << "):\t" << registers[i];
break;
default:
cout << "\t" << i << ":\t" << registers[i];
break;
}
if ((i % 4) == 3) { cout << "\n"; }
}
cout << "\tHi:\t" << registers[HiReg];
cout << "\tLo:\t" << registers[LoReg];
cout << "\tPC:\t" << registers[PCReg];
cout << "\tNextPC:\t" << registers[NextPCReg];
cout << "\tPrevPC:\t" << registers[PrevPCReg];
cout << "\tLoad:\t" << registers[LoadReg];
cout << "\tLoadV:\t" << registers[LoadValueReg] << "\n";
}
//----------------------------------------------------------------------
// Machine::ReadRegister/WriteRegister
// Fetch or write the contents of a user program register.
//----------------------------------------------------------------------
int
Machine::ReadRegister(int num)
{
ASSERT((num >= 0) && (num < NumTotalRegs));
return registers[num];
}
void
Machine::WriteRegister(int num, int value)
{
ASSERT((num >= 0) && (num < NumTotalRegs));
registers[num] = value;
}

206
code/machine/machine.h Executable file
View File

@@ -0,0 +1,206 @@
// machine.h
// Data structures for simulating the execution of user programs
// running on top of Nachos.
//
// User programs are loaded into "mainMemory"; to Nachos,
// this looks just like an array of bytes. Of course, the Nachos
// kernel is in memory too -- but as in most machines these days,
// the kernel is loaded into a separate memory region from user
// programs, and accesses to kernel memory are not translated or paged.
//
// In Nachos, user programs are executed one instruction at a time,
// by the simulator. Each memory reference is translated, checked
// for errors, etc.
//
// DO NOT CHANGE EXCEPT AS NOTED BELOW -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef MACHINE_H
#define MACHINE_H
#include "copyright.h"
#include "utility.h"
#include "translate.h"
// Definitions related to the size, and format of user memory
const int PageSize = 128; // set the page size equal to
// the disk sector size, for simplicity
//
// You are allowed to change this value.
// Doing so will change the number of pages of physical memory
// available on the simulated machine.
//
const int NumPhysPages = 128;
const int MemorySize = (NumPhysPages * PageSize);
const int TLBSize = 4; // if there is a TLB, make it small
enum ExceptionType { NoException, // Everything ok!
SyscallException, // A program executed a system call.
PageFaultException, // No valid translation found
ReadOnlyException, // Write attempted to page marked
// "read-only"
BusErrorException, // Translation resulted in an
// invalid physical address
AddressErrorException, // Unaligned reference or one that
// was beyond the end of the
// address space
OverflowException, // Integer overflow in add or sub.
IllegalInstrException, // Unimplemented or reserved instr.
NumExceptionTypes
};
// User program CPU state. The full set of MIPS registers, plus a few
// more because we need to be able to start/stop a user program between
// any two instructions (thus we need to keep track of things like load
// delay slots, etc.)
#define StackReg 29 // User's stack pointer
#define RetAddrReg 31 // Holds return address for procedure calls
#define NumGPRegs 32 // 32 general purpose registers on MIPS
#define HiReg 32 // Double register to hold multiply result
#define LoReg 33
#define PCReg 34 // Current program counter
#define NextPCReg 35 // Next program counter (for branch delay)
#define PrevPCReg 36 // Previous program counter (for debugging)
#define LoadReg 37 // The register target of a delayed load.
#define LoadValueReg 38 // The value to be loaded by a delayed load.
#define BadVAddrReg 39 // The failing virtual address on an exception
#define NumTotalRegs 40
// The following class defines the simulated host workstation hardware, as
// seen by user programs -- the CPU registers, main memory, etc.
// User programs shouldn't be able to tell that they are running on our
// simulator or on the real hardware, except
// we don't support floating point instructions
// the system call interface to Nachos is not the same as UNIX
// (10 system calls in Nachos vs. 200 in UNIX!)
// If we were to implement more of the UNIX system calls, we ought to be
// able to run Nachos on top of Nachos!
//
// The procedures in this class are defined in machine.cc, mipssim.cc, and
// translate.cc.
class Instruction;
class Interrupt;
class Machine {
public:
Machine(bool debug); // Initialize the simulation of the hardware
// for running user programs
~Machine(); // De-allocate the data structures
// Routines callable by the Nachos kernel
void Run(); // Run a user program
int ReadRegister(int num); // read the contents of a CPU register
void WriteRegister(int num, int value);
// store a value into a CPU register
// Data structures accessible to the Nachos kernel -- main memory and the
// page table/TLB.
//
// Note that *all* communication between the user program and the kernel
// are in terms of these data structures (plus the CPU registers).
char *mainMemory; // physical memory to store user program,
// code and data, while executing
// NOTE: the hardware translation of virtual addresses in the user program
// to physical addresses (relative to the beginning of "mainMemory")
// can be controlled by one of:
// a traditional linear page table
// a software-loaded translation lookaside buffer (tlb) -- a cache of
// mappings of virtual page #'s to physical page #'s
//
// If "tlb" is NULL, the linear page table is used
// If "tlb" is non-NULL, the Nachos kernel is responsible for managing
// the contents of the TLB. But the kernel can use any data structure
// it wants (eg, segmented paging) for handling TLB cache misses.
//
// For simplicity, both the page table pointer and the TLB pointer are
// public. However, while there can be multiple page tables (one per address
// space, stored in memory), there is only one TLB (implemented in hardware).
// Thus the TLB pointer should be considered as *read-only*, although
// the contents of the TLB are free to be modified by the kernel software.
TranslationEntry *tlb; // this pointer should be considered
// "read-only" to Nachos kernel code
TranslationEntry *pageTable;
unsigned int pageTableSize;
bool ReadMem(int addr, int size, int* value);
bool WriteMem(int addr, int size, int value);
// Read or write 1, 2, or 4 bytes of virtual
// memory (at addr). Return FALSE if a
// correct translation couldn't be found.
private:
// Routines internal to the machine simulation -- DO NOT call these directly
void DelayedLoad(int nextReg, int nextVal);
// Do a pending delayed load (modifying a reg)
void OneInstruction(Instruction *instr);
// Run one instruction of a user program.
ExceptionType Translate(int virtAddr, int* physAddr, int size,bool writing);
// Translate an address, and check for
// alignment. Set the use and dirty bits in
// the translation entry appropriately,
// and return an exception code if the
// translation couldn't be completed.
void RaiseException(ExceptionType which, int badVAddr);
// Trap to the Nachos kernel, because of a
// system call or other exception.
void Debugger(); // invoke the user program debugger
void DumpState(); // print the user CPU and memory state
// Internal data structures
int registers[NumTotalRegs]; // CPU registers, for executing user programs
bool singleStep; // drop back into the debugger after each
// simulated instruction
int runUntilTime; // drop back into the debugger when simulated
// time reaches this value
friend class Interrupt; // calls DelayedLoad()
};
extern void ExceptionHandler(ExceptionType which);
// Entry point into Nachos for handling
// user system calls and exceptions
// Defined in exception.cc
// Routines for converting Words and Short Words to and from the
// simulated machine's format of little endian. If the host machine
// is little endian (DEC and Intel), these end up being NOPs.
//
// What is stored in each format:
// host byte ordering:
// kernel data structures
// user registers
// simulated machine byte ordering:
// contents of main memory
unsigned int WordToHost(unsigned int word);
unsigned short ShortToHost(unsigned short shortword);
unsigned int WordToMachine(unsigned int word);
unsigned short ShortToMachine(unsigned short shortword);
#endif // MACHINE_H

828
code/machine/mipssim.cc Executable file
View File

@@ -0,0 +1,828 @@
// mipssim.cc -- simulate a MIPS R2/3000 processor
//
// This code has been adapted from Ousterhout's MIPSSIM package.
// Byte ordering is little-endian, so we can be compatible with
// DEC RISC systems.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
// Simulation fixes done by Peter E Reissner, class of Winter 1994/95 (York)
// I've not been able to test this extensively.
// Ported to newer version of Nachos at Waterloo by Scott Graham (Mar 99).
#include "copyright.h"
#include "debug.h"
#include "machine.h"
#include "mipssim.h"
#include "main.h"
static void Mult(int a, int b, bool signedArith, int* hiPtr, int* loPtr);
// The following class defines an instruction, represented in both
// undecoded binary form
// decoded to identify
// operation to do
// registers to act on
// any immediate operand value
class Instruction {
public:
void Decode(); // decode the binary representation of the instruction
unsigned int value; // binary representation of the instruction
char opCode; // Type of instruction. This is NOT the same as the
// opcode field from the instruction: see defs in mips.h
char rs, rt, rd; // Three registers from instruction.
int extra; // Immediate or target or shamt field or offset.
// Immediates are sign-extended.
};
//----------------------------------------------------------------------
// Machine::Run
// Simulate the execution of a user-level program on Nachos.
// Called by the kernel when the program starts up; never returns.
//
// This routine is re-entrant, in that it can be called multiple
// times concurrently -- one for each thread executing user code.
//----------------------------------------------------------------------
void
Machine::Run()
{
Instruction *instr = new Instruction; // storage for decoded instruction
if (debug->IsEnabled('m')) {
cout << "Starting program in thread: " << kernel->currentThread->getName();
cout << ", at time: " << kernel->stats->totalTicks << "\n";
}
kernel->interrupt->setStatus(UserMode);
for (;;) {
OneInstruction(instr);
kernel->interrupt->OneTick();
if (singleStep && (runUntilTime <= kernel->stats->totalTicks))
Debugger();
}
}
//----------------------------------------------------------------------
// TypeToReg
// Retrieve the register # referred to in an instruction.
//----------------------------------------------------------------------
static int
TypeToReg(RegType reg, Instruction *instr)
{
switch (reg) {
case RS:
return instr->rs;
case RT:
return instr->rt;
case RD:
return instr->rd;
case EXTRA:
return instr->extra;
default:
return -1;
}
}
//----------------------------------------------------------------------
// Machine::OneInstruction
// Execute one instruction from a user-level program
//
// If there is any kind of exception or interrupt, we invoke the
// exception handler, and when it returns, we return to Run(), which
// will re-invoke us in a loop. This allows us to
// re-start the instruction execution from the beginning, in
// case any of our state has changed. On a syscall,
// the OS software must increment the PC so execution begins
// at the instruction immediately after the syscall.
//
// This routine is re-entrant, in that it can be called multiple
// times concurrently -- one for each thread executing user code.
// We get re-entrancy by never caching any data -- we always re-start the
// simulation from scratch each time we are called (or after trapping
// back to the Nachos kernel on an exception or interrupt), and we always
// store all data back to the machine registers and memory before
// leaving. This allows the Nachos kernel to control our behavior
// by controlling the contents of memory, the translation table,
// and the register set.
//----------------------------------------------------------------------
void
Machine::OneInstruction(Instruction *instr)
{
#ifdef SIM_FIX
int byte; // described in Kane for LWL,LWR,...
#endif
int raw;
int nextLoadReg = 0;
int nextLoadValue = 0; // record delayed load operation, to apply
// in the future
// Fetch instruction
if (!ReadMem(registers[PCReg], 4, &raw))
return; // exception occurred
instr->value = raw;
instr->Decode();
if (debug->IsEnabled('m')) {
struct OpString *str = &opStrings[instr->opCode];
char buf[80];
ASSERT(instr->opCode <= MaxOpcode);
cout << "At PC = " << registers[PCReg];
sprintf(buf, str->format, TypeToReg(str->args[0], instr),
TypeToReg(str->args[1], instr), TypeToReg(str->args[2], instr));
cout << "\t" << buf << "\n";
}
// Compute next pc, but don't install in case there's an error or branch.
int pcAfter = registers[NextPCReg] + 4;
int sum, diff, tmp, value;
unsigned int rs, rt, imm;
// Execute the instruction (cf. Kane's book)
switch (instr->opCode) {
case OP_ADD:
sum = registers[instr->rs] + registers[instr->rt];
if (!((registers[instr->rs] ^ registers[instr->rt]) & SIGN_BIT) &&
((registers[instr->rs] ^ sum) & SIGN_BIT)) {
RaiseException(OverflowException, 0);
return;
}
registers[instr->rd] = sum;
break;
case OP_ADDI:
sum = registers[instr->rs] + instr->extra;
if (!((registers[instr->rs] ^ instr->extra) & SIGN_BIT) &&
((instr->extra ^ sum) & SIGN_BIT)) {
RaiseException(OverflowException, 0);
return;
}
registers[instr->rt] = sum;
break;
case OP_ADDIU:
registers[instr->rt] = registers[instr->rs] + instr->extra;
break;
case OP_ADDU:
registers[instr->rd] = registers[instr->rs] + registers[instr->rt];
break;
case OP_AND:
registers[instr->rd] = registers[instr->rs] & registers[instr->rt];
break;
case OP_ANDI:
registers[instr->rt] = registers[instr->rs] & (instr->extra & 0xffff);
break;
case OP_BEQ:
if (registers[instr->rs] == registers[instr->rt])
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
break;
case OP_BGEZAL:
registers[R31] = registers[NextPCReg] + 4;
case OP_BGEZ:
if (!(registers[instr->rs] & SIGN_BIT))
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
break;
case OP_BGTZ:
if (registers[instr->rs] > 0)
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
break;
case OP_BLEZ:
if (registers[instr->rs] <= 0)
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
break;
case OP_BLTZAL:
registers[R31] = registers[NextPCReg] + 4;
case OP_BLTZ:
if (registers[instr->rs] & SIGN_BIT)
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
break;
case OP_BNE:
if (registers[instr->rs] != registers[instr->rt])
pcAfter = registers[NextPCReg] + IndexToAddr(instr->extra);
break;
case OP_DIV:
if (registers[instr->rt] == 0) {
registers[LoReg] = 0;
registers[HiReg] = 0;
} else {
registers[LoReg] = registers[instr->rs] / registers[instr->rt];
registers[HiReg] = registers[instr->rs] % registers[instr->rt];
}
break;
case OP_DIVU:
rs = (unsigned int) registers[instr->rs];
rt = (unsigned int) registers[instr->rt];
if (rt == 0) {
registers[LoReg] = 0;
registers[HiReg] = 0;
} else {
tmp = rs / rt;
registers[LoReg] = (int) tmp;
tmp = rs % rt;
registers[HiReg] = (int) tmp;
}
break;
case OP_JAL:
registers[R31] = registers[NextPCReg] + 4;
case OP_J:
pcAfter = (pcAfter & 0xf0000000) | IndexToAddr(instr->extra);
break;
case OP_JALR:
registers[instr->rd] = registers[NextPCReg] + 4;
case OP_JR:
pcAfter = registers[instr->rs];
break;
case OP_LB:
case OP_LBU:
tmp = registers[instr->rs] + instr->extra;
if (!ReadMem(tmp, 1, &value))
return;
if ((value & 0x80) && (instr->opCode == OP_LB))
value |= 0xffffff00;
else
value &= 0xff;
nextLoadReg = instr->rt;
nextLoadValue = value;
break;
case OP_LH:
case OP_LHU:
tmp = registers[instr->rs] + instr->extra;
if (tmp & 0x1) {
RaiseException(AddressErrorException, tmp);
return;
}
if (!ReadMem(tmp, 2, &value))
return;
if ((value & 0x8000) && (instr->opCode == OP_LH))
value |= 0xffff0000;
else
value &= 0xffff;
nextLoadReg = instr->rt;
nextLoadValue = value;
break;
case OP_LUI:
DEBUG(dbgMach, "Executing: LUI r" << instr->rt << ", " << instr->extra);
registers[instr->rt] = instr->extra << 16;
break;
case OP_LW:
tmp = registers[instr->rs] + instr->extra;
if (tmp & 0x3) {
RaiseException(AddressErrorException, tmp);
return;
}
if (!ReadMem(tmp, 4, &value))
return;
nextLoadReg = instr->rt;
nextLoadValue = value;
break;
case OP_LWL:
tmp = registers[instr->rs] + instr->extra;
#ifdef SIM_FIX
// The only difference between this code and the BIG ENDIAN code
// is that the ReadMem call is guaranteed an aligned access as it
// should be (Kane's book hides the fact that all memory access
// are done using aligned loads - what the instruction asks for
// is a arbitrary) This is the whole purpose of LWL and LWR etc.
// Then the switch uses 3 - (tmp & 0x3) instead of (tmp & 0x3)
byte = tmp & 0x3;
// DEBUG('P', "Addr 0x%X\n",tmp-byte);
if (!ReadMem(tmp-byte, 4, &value))
return;
#else
// ReadMem assumes all 4 byte requests are aligned on an even
// word boundary. Also, the little endian/big endian swap code would
// fail (I think) if the other cases are ever exercised.
ASSERT((tmp & 0x3) == 0);
if (!ReadMem(tmp, 4, &value))
return;
#endif
if (registers[LoadReg] == instr->rt)
nextLoadValue = registers[LoadValueReg];
else
nextLoadValue = registers[instr->rt];
#ifdef SIM_FIX
switch (3 - byte)
#else
switch (tmp & 0x3)
#endif
{
case 0:
nextLoadValue = value;
break;
case 1:
nextLoadValue = (nextLoadValue & 0xff) | (value << 8);
break;
case 2:
nextLoadValue = (nextLoadValue & 0xffff) | (value << 16);
break;
case 3:
nextLoadValue = (nextLoadValue & 0xffffff) | (value << 24);
break;
}
nextLoadReg = instr->rt;
break;
case OP_LWR:
tmp = registers[instr->rs] + instr->extra;
#ifdef SIM_FIX
// The only difference between this code and the BIG ENDIAN code
// is that the ReadMem call is guaranteed an aligned access as it
// should be (Kane's book hides the fact that all memory access
// are done using aligned loads - what the instruction asks
// for is a arbitrary) This is the whole purpose of LWL and LWR etc.
// Then the switch uses 3 - (tmp & 0x3) instead of (tmp & 0x3)
byte = tmp & 0x3;
// DEBUG('P', "Addr 0x%X\n",tmp-byte);
if (!ReadMem(tmp-byte, 4, &value))
return;
#else
// ReadMem assumes all 4 byte requests are aligned on an even
// word boundary. Also, the little endian/big endian swap code would
// fail (I think) if the other cases are ever exercised.
ASSERT((tmp & 0x3) == 0);
if (!ReadMem(tmp, 4, &value))
return;
#endif
if (registers[LoadReg] == instr->rt)
nextLoadValue = registers[LoadValueReg];
else
nextLoadValue = registers[instr->rt];
#ifdef SIM_FIX
switch (3 - byte)
#else
switch (tmp & 0x3)
#endif
{
case 0:
nextLoadValue = (nextLoadValue & 0xffffff00) |
((value >> 24) & 0xff);
break;
case 1:
nextLoadValue = (nextLoadValue & 0xffff0000) |
((value >> 16) & 0xffff);
break;
case 2:
nextLoadValue = (nextLoadValue & 0xff000000)
| ((value >> 8) & 0xffffff);
break;
case 3:
nextLoadValue = value;
break;
}
nextLoadReg = instr->rt;
break;
case OP_MFHI:
registers[instr->rd] = registers[HiReg];
break;
case OP_MFLO:
registers[instr->rd] = registers[LoReg];
break;
case OP_MTHI:
registers[HiReg] = registers[instr->rs];
break;
case OP_MTLO:
registers[LoReg] = registers[instr->rs];
break;
case OP_MULT:
Mult(registers[instr->rs], registers[instr->rt], TRUE,
&registers[HiReg], &registers[LoReg]);
break;
case OP_MULTU:
Mult(registers[instr->rs], registers[instr->rt], FALSE,
&registers[HiReg], &registers[LoReg]);
break;
case OP_NOR:
registers[instr->rd] = ~(registers[instr->rs] | registers[instr->rt]);
break;
case OP_OR:
registers[instr->rd] = registers[instr->rs] | registers[instr->rt];
break;
case OP_ORI:
registers[instr->rt] = registers[instr->rs] | (instr->extra & 0xffff);
break;
case OP_SB:
if (!WriteMem((unsigned)
(registers[instr->rs] + instr->extra), 1, registers[instr->rt]))
return;
break;
case OP_SH:
if (!WriteMem((unsigned)
(registers[instr->rs] + instr->extra), 2, registers[instr->rt]))
return;
break;
case OP_SLL:
registers[instr->rd] = registers[instr->rt] << instr->extra;
break;
case OP_SLLV:
registers[instr->rd] = registers[instr->rt] <<
(registers[instr->rs] & 0x1f);
break;
case OP_SLT:
if (registers[instr->rs] < registers[instr->rt])
registers[instr->rd] = 1;
else
registers[instr->rd] = 0;
break;
case OP_SLTI:
if (registers[instr->rs] < instr->extra)
registers[instr->rt] = 1;
else
registers[instr->rt] = 0;
break;
case OP_SLTIU:
rs = registers[instr->rs];
imm = instr->extra;
if (rs < imm)
registers[instr->rt] = 1;
else
registers[instr->rt] = 0;
break;
case OP_SLTU:
rs = registers[instr->rs];
rt = registers[instr->rt];
if (rs < rt)
registers[instr->rd] = 1;
else
registers[instr->rd] = 0;
break;
case OP_SRA:
registers[instr->rd] = registers[instr->rt] >> instr->extra;
break;
case OP_SRAV:
registers[instr->rd] = registers[instr->rt] >>
(registers[instr->rs] & 0x1f);
break;
case OP_SRL:
tmp = registers[instr->rt];
tmp >>= instr->extra;
registers[instr->rd] = tmp;
break;
case OP_SRLV:
tmp = registers[instr->rt];
tmp >>= (registers[instr->rs] & 0x1f);
registers[instr->rd] = tmp;
break;
case OP_SUB:
diff = registers[instr->rs] - registers[instr->rt];
if (((registers[instr->rs] ^ registers[instr->rt]) & SIGN_BIT) &&
((registers[instr->rs] ^ diff) & SIGN_BIT)) {
RaiseException(OverflowException, 0);
return;
}
registers[instr->rd] = diff;
break;
case OP_SUBU:
registers[instr->rd] = registers[instr->rs] - registers[instr->rt];
break;
case OP_SW:
if (!WriteMem((unsigned)
(registers[instr->rs] + instr->extra), 4, registers[instr->rt]))
return;
break;
case OP_SWL:
tmp = registers[instr->rs] + instr->extra;
#ifdef SIM_FIX
// The only difference between this code and the BIG ENDIAN code
// is that the ReadMem call is guaranteed an aligned access as it
// should be (Kane's book hides the fact that all memory access
// are done using aligned loads - what the instruction asks for
// is a arbitrary) This is the whole purpose of LWL and LWR etc.
byte = tmp & 0x3;
// DEBUG('P', "Addr 0x%X\n",tmp-byte);
if (!ReadMem(tmp-byte, 4, &value))
return;
// DEBUG('P', "Value 0x%X\n",value);
#else
// The little endian/big endian swap code would
// fail (I think) if the other cases are ever exercised.
ASSERT((tmp & 0x3) == 0);
if (!ReadMem((tmp & ~0x3), 4, &value))
return;
#endif
#ifdef SIM_FIX
switch( 3 - byte )
#else
switch (tmp & 0x3)
#endif // SIM_FIX
{
case 0:
value = registers[instr->rt];
break;
case 1:
value = (value & 0xff000000) | ((registers[instr->rt] >> 8) &
0xffffff);
break;
case 2:
value = (value & 0xffff0000) | ((registers[instr->rt] >> 16) &
0xffff);
break;
case 3:
value = (value & 0xffffff00) | ((registers[instr->rt] >> 24) &
0xff);
break;
}
#ifndef SIM_FIX
if (!WriteMem((tmp & ~0x3), 4, value))
return;
#else
// DEBUG('P', "Value 0x%X\n",value);
if (!WriteMem((tmp - byte), 4, value))
return;
#endif // SIM_FIX
break;
case OP_SWR:
tmp = registers[instr->rs] + instr->extra;
#ifndef SIM_FIX
// The little endian/big endian swap code would
// fail (I think) if the other cases are ever exercised.
ASSERT((tmp & 0x3) == 0);
if (!ReadMem((tmp & ~0x3), 4, &value))
return;
#else
// The only difference between this code and the BIG ENDIAN code
// is that the ReadMem call is guaranteed an aligned access as
// it should be (Kane's book hides the fact that all memory
// access are done using aligned loads - what the instruction
// asks for is a arbitrary) This is the whole purpose of LWL
// and LWR etc.
byte = tmp & 0x3;
// DEBUG('P', "Addr 0x%X\n",tmp-byte);
if (!ReadMem(tmp-byte, 4, &value))
return;
// DEBUG('P', "Value 0x%X\n",value);
#endif // SIM_FIX
#ifndef SIM_FIX
switch (tmp & 0x3)
#else
switch( 3 - byte )
#endif // SIM_FIX
{
case 0:
value = (value & 0xffffff) | (registers[instr->rt] << 24);
break;
case 1:
value = (value & 0xffff) | (registers[instr->rt] << 16);
break;
case 2:
value = (value & 0xff) | (registers[instr->rt] << 8);
break;
case 3:
value = registers[instr->rt];
break;
}
#ifndef SIM_FIX
if (!WriteMem((tmp & ~0x3), 4, value))
return;
#else
// DEBUG('P', "Value 0x%X\n",value);
if (!WriteMem((tmp - byte), 4, value))
return;
#endif // SIM_FIX
break;
case OP_SYSCALL:
RaiseException(SyscallException, 0);
return;
case OP_XOR:
registers[instr->rd] = registers[instr->rs] ^ registers[instr->rt];
break;
case OP_XORI:
registers[instr->rt] = registers[instr->rs] ^ (instr->extra & 0xffff);
break;
case OP_RES:
case OP_UNIMP:
RaiseException(IllegalInstrException, 0);
return;
default:
ASSERT(FALSE);
}
// Now we have successfully executed the instruction.
// Do any delayed load operation
DelayedLoad(nextLoadReg, nextLoadValue);
// Advance program counters.
registers[PrevPCReg] = registers[PCReg]; // for debugging, in case we
// are jumping into lala-land
registers[PCReg] = registers[NextPCReg];
registers[NextPCReg] = pcAfter;
}
//----------------------------------------------------------------------
// Machine::DelayedLoad
// Simulate effects of a delayed load.
//
// NOTE -- RaiseException/CheckInterrupts must also call DelayedLoad,
// since any delayed load must get applied before we trap to the kernel.
//----------------------------------------------------------------------
void
Machine::DelayedLoad(int nextReg, int nextValue)
{
registers[registers[LoadReg]] = registers[LoadValueReg];
registers[LoadReg] = nextReg;
registers[LoadValueReg] = nextValue;
registers[0] = 0; // and always make sure R0 stays zero.
}
//----------------------------------------------------------------------
// Instruction::Decode
// Decode a MIPS instruction
//----------------------------------------------------------------------
void
Instruction::Decode()
{
OpInfo *opPtr;
rs = (value >> 21) & 0x1f;
rt = (value >> 16) & 0x1f;
rd = (value >> 11) & 0x1f;
opPtr = &opTable[(value >> 26) & 0x3f];
opCode = opPtr->opCode;
if (opPtr->format == IFMT) {
extra = value & 0xffff;
if (extra & 0x8000) {
extra |= 0xffff0000;
}
} else if (opPtr->format == RFMT) {
extra = (value >> 6) & 0x1f;
} else {
extra = value & 0x3ffffff;
}
if (opCode == SPECIAL) {
opCode = specialTable[value & 0x3f];
} else if (opCode == BCOND) {
int i = value & 0x1f0000;
if (i == 0) {
opCode = OP_BLTZ;
} else if (i == 0x10000) {
opCode = OP_BGEZ;
} else if (i == 0x100000) {
opCode = OP_BLTZAL;
} else if (i == 0x110000) {
opCode = OP_BGEZAL;
} else {
opCode = OP_UNIMP;
}
}
}
//----------------------------------------------------------------------
// Mult
// Simulate R2000 multiplication.
// The words at *hiPtr and *loPtr are overwritten with the
// double-length result of the multiplication.
//----------------------------------------------------------------------
static void
Mult(int a, int b, bool signedArith, int* hiPtr, int* loPtr)
{
if ((a == 0) || (b == 0)) {
*hiPtr = *loPtr = 0;
return;
}
// Compute the sign of the result, then make everything positive
// so unsigned computation can be done in the main loop.
bool negative = FALSE;
if (signedArith) {
if (a < 0) {
negative = !negative;
a = -a;
}
if (b < 0) {
negative = !negative;
b = -b;
}
}
// Compute the result in unsigned arithmetic (check a's bits one at
// a time, and add in a shifted value of b).
unsigned int bLo = b;
unsigned int bHi = 0;
unsigned int lo = 0;
unsigned int hi = 0;
for (int i = 0; i < 32; i++) {
if (a & 1) {
lo += bLo;
if (lo < bLo) // Carry out of the low bits?
hi += 1;
hi += bHi;
if ((a & 0xfffffffe) == 0)
break;
}
bHi <<= 1;
if (bLo & 0x80000000)
bHi |= 1;
bLo <<= 1;
a >>= 1;
}
// If the result is supposed to be negative, compute the two's
// complement of the double-word result.
if (negative) {
hi = ~hi;
lo = ~lo;
lo++;
if (lo == 0)
hi++;
}
*hiPtr = (int) hi;
*loPtr = (int) lo;
}

229
code/machine/mipssim.h Executable file
View File

@@ -0,0 +1,229 @@
// mipssim.h
// Internal data structures for simulating the MIPS instruction set.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef MIPSSIM_H
#define MIPSSIM_H
#include "copyright.h"
/*
* OpCode values. The names are straight from the MIPS
* manual except for the following special ones:
*
* OP_UNIMP - means that this instruction is legal, but hasn't
* been implemented in the simulator yet.
* OP_RES - means that this is a reserved opcode (it isn't
* supported by the architecture).
*/
#define OP_ADD 1
#define OP_ADDI 2
#define OP_ADDIU 3
#define OP_ADDU 4
#define OP_AND 5
#define OP_ANDI 6
#define OP_BEQ 7
#define OP_BGEZ 8
#define OP_BGEZAL 9
#define OP_BGTZ 10
#define OP_BLEZ 11
#define OP_BLTZ 12
#define OP_BLTZAL 13
#define OP_BNE 14
#define OP_DIV 16
#define OP_DIVU 17
#define OP_J 18
#define OP_JAL 19
#define OP_JALR 20
#define OP_JR 21
#define OP_LB 22
#define OP_LBU 23
#define OP_LH 24
#define OP_LHU 25
#define OP_LUI 26
#define OP_LW 27
#define OP_LWL 28
#define OP_LWR 29
#define OP_MFHI 31
#define OP_MFLO 32
#define OP_MTHI 34
#define OP_MTLO 35
#define OP_MULT 36
#define OP_MULTU 37
#define OP_NOR 38
#define OP_OR 39
#define OP_ORI 40
#define OP_RFE 41
#define OP_SB 42
#define OP_SH 43
#define OP_SLL 44
#define OP_SLLV 45
#define OP_SLT 46
#define OP_SLTI 47
#define OP_SLTIU 48
#define OP_SLTU 49
#define OP_SRA 50
#define OP_SRAV 51
#define OP_SRL 52
#define OP_SRLV 53
#define OP_SUB 54
#define OP_SUBU 55
#define OP_SW 56
#define OP_SWL 57
#define OP_SWR 58
#define OP_XOR 59
#define OP_XORI 60
#define OP_SYSCALL 61
#define OP_UNIMP 62
#define OP_RES 63
#define MaxOpcode 63
/*
* Miscellaneous definitions:
*/
#define IndexToAddr(x) ((x) << 2)
#define SIGN_BIT 0x80000000
#define R31 31
/*
* The table below is used to translate bits 31:26 of the instruction
* into a value suitable for the "opCode" field of a MemWord structure,
* or into a special value for further decoding.
*/
#define SPECIAL 100
#define BCOND 101
#define IFMT 1
#define JFMT 2
#define RFMT 3
struct OpInfo {
int opCode; /* Translated op code. */
int format; /* Format type (IFMT or JFMT or RFMT) */
};
static OpInfo opTable[] = {
{SPECIAL, RFMT}, {BCOND, IFMT}, {OP_J, JFMT}, {OP_JAL, JFMT},
{OP_BEQ, IFMT}, {OP_BNE, IFMT}, {OP_BLEZ, IFMT}, {OP_BGTZ, IFMT},
{OP_ADDI, IFMT}, {OP_ADDIU, IFMT}, {OP_SLTI, IFMT}, {OP_SLTIU, IFMT},
{OP_ANDI, IFMT}, {OP_ORI, IFMT}, {OP_XORI, IFMT}, {OP_LUI, IFMT},
{OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT},
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT},
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT},
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT},
{OP_LB, IFMT}, {OP_LH, IFMT}, {OP_LWL, IFMT}, {OP_LW, IFMT},
{OP_LBU, IFMT}, {OP_LHU, IFMT}, {OP_LWR, IFMT}, {OP_RES, IFMT},
{OP_SB, IFMT}, {OP_SH, IFMT}, {OP_SWL, IFMT}, {OP_SW, IFMT},
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_SWR, IFMT}, {OP_RES, IFMT},
{OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT},
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT},
{OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT}, {OP_UNIMP, IFMT},
{OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}, {OP_RES, IFMT}
};
/*
* The table below is used to convert the "funct" field of SPECIAL
* instructions into the "opCode" field of a MemWord.
*/
static int specialTable[] = {
OP_SLL, OP_RES, OP_SRL, OP_SRA, OP_SLLV, OP_RES, OP_SRLV, OP_SRAV,
OP_JR, OP_JALR, OP_RES, OP_RES, OP_SYSCALL, OP_UNIMP, OP_RES, OP_RES,
OP_MFHI, OP_MTHI, OP_MFLO, OP_MTLO, OP_RES, OP_RES, OP_RES, OP_RES,
OP_MULT, OP_MULTU, OP_DIV, OP_DIVU, OP_RES, OP_RES, OP_RES, OP_RES,
OP_ADD, OP_ADDU, OP_SUB, OP_SUBU, OP_AND, OP_OR, OP_XOR, OP_NOR,
OP_RES, OP_RES, OP_SLT, OP_SLTU, OP_RES, OP_RES, OP_RES, OP_RES,
OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES,
OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES, OP_RES
};
// Stuff to help print out each instruction, for debugging
enum RegType { NONE, RS, RT, RD, EXTRA };
struct OpString {
char *format; // Printed version of instruction
RegType args[3];
};
static struct OpString opStrings[] = {
{"Shouldn't happen", {NONE, NONE, NONE}},
{"ADD r%d,r%d,r%d", {RD, RS, RT}},
{"ADDI r%d,r%d,%d", {RT, RS, EXTRA}},
{"ADDIU r%d,r%d,%d", {RT, RS, EXTRA}},
{"ADDU r%d,r%d,r%d", {RD, RS, RT}},
{"AND r%d,r%d,r%d", {RD, RS, RT}},
{"ANDI r%d,r%d,%d", {RT, RS, EXTRA}},
{"BEQ r%d,r%d,%d", {RS, RT, EXTRA}},
{"BGEZ r%d,%d", {RS, EXTRA, NONE}},
{"BGEZAL r%d,%d", {RS, EXTRA, NONE}},
{"BGTZ r%d,%d", {RS, EXTRA, NONE}},
{"BLEZ r%d,%d", {RS, EXTRA, NONE}},
{"BLTZ r%d,%d", {RS, EXTRA, NONE}},
{"BLTZAL r%d,%d", {RS, EXTRA, NONE}},
{"BNE r%d,r%d,%d", {RS, RT, EXTRA}},
{"Shouldn't happen", {NONE, NONE, NONE}},
{"DIV r%d,r%d", {RS, RT, NONE}},
{"DIVU r%d,r%d", {RS, RT, NONE}},
{"J %d", {EXTRA, NONE, NONE}},
{"JAL %d", {EXTRA, NONE, NONE}},
{"JALR r%d,r%d", {RD, RS, NONE}},
{"JR r%d,r%d", {RD, RS, NONE}},
{"LB r%d,%d(r%d)", {RT, EXTRA, RS}},
{"LBU r%d,%d(r%d)", {RT, EXTRA, RS}},
{"LH r%d,%d(r%d)", {RT, EXTRA, RS}},
{"LHU r%d,%d(r%d)", {RT, EXTRA, RS}},
{"LUI r%d,%d", {RT, EXTRA, NONE}},
{"LW r%d,%d(r%d)", {RT, EXTRA, RS}},
{"LWL r%d,%d(r%d)", {RT, EXTRA, RS}},
{"LWR r%d,%d(r%d)", {RT, EXTRA, RS}},
{"Shouldn't happen", {NONE, NONE, NONE}},
{"MFHI r%d", {RD, NONE, NONE}},
{"MFLO r%d", {RD, NONE, NONE}},
{"Shouldn't happen", {NONE, NONE, NONE}},
{"MTHI r%d", {RS, NONE, NONE}},
{"MTLO r%d", {RS, NONE, NONE}},
{"MULT r%d,r%d", {RS, RT, NONE}},
{"MULTU r%d,r%d", {RS, RT, NONE}},
{"NOR r%d,r%d,r%d", {RD, RS, RT}},
{"OR r%d,r%d,r%d", {RD, RS, RT}},
{"ORI r%d,r%d,%d", {RT, RS, EXTRA}},
{"RFE", {NONE, NONE, NONE}},
{"SB r%d,%d(r%d)", {RT, EXTRA, RS}},
{"SH r%d,%d(r%d)", {RT, EXTRA, RS}},
{"SLL r%d,r%d,%d", {RD, RT, EXTRA}},
{"SLLV r%d,r%d,r%d", {RD, RT, RS}},
{"SLT r%d,r%d,r%d", {RD, RS, RT}},
{"SLTI r%d,r%d,%d", {RT, RS, EXTRA}},
{"SLTIU r%d,r%d,%d", {RT, RS, EXTRA}},
{"SLTU r%d,r%d,r%d", {RD, RS, RT}},
{"SRA r%d,r%d,%d", {RD, RT, EXTRA}},
{"SRAV r%d,r%d,r%d", {RD, RT, RS}},
{"SRL r%d,r%d,%d", {RD, RT, EXTRA}},
{"SRLV r%d,r%d,r%d", {RD, RT, RS}},
{"SUB r%d,r%d,r%d", {RD, RS, RT}},
{"SUBU r%d,r%d,r%d", {RD, RS, RT}},
{"SW r%d,%d(r%d)", {RT, EXTRA, RS}},
{"SWL r%d,%d(r%d)", {RT, EXTRA, RS}},
{"SWR r%d,%d(r%d)", {RT, EXTRA, RS}},
{"XOR r%d,r%d,r%d", {RD, RS, RT}},
{"XORI r%d,r%d,%d", {RT, RS, EXTRA}},
{"SYSCALL", {NONE, NONE, NONE}},
{"Unimplemented", {NONE, NONE, NONE}},
{"Reserved", {NONE, NONE, NONE}}
};
#endif // MIPSSIM_H

182
code/machine/network.cc Executable file
View File

@@ -0,0 +1,182 @@
// network.cc
// Routines to simulate a network interface, using UNIX sockets
// to deliver packets between multiple invocations of nachos.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "network.h"
#include "main.h"
//-----------------------------------------------------------------------
// NetworkInput::NetworkInput
// Initialize the simulation for the network input
//
// "toCall" is the interrupt handler to call when packet arrives
//-----------------------------------------------------------------------
NetworkInput::NetworkInput(CallBackObj *toCall)
{
// set up the stuff to emulate asynchronous interrupts
callWhenAvail = toCall;
packetAvail = FALSE;
inHdr.length = 0;
sock = OpenSocket();
sprintf(sockName, "SOCKET_%d", kernel->hostName);
AssignNameToSocket(sockName, sock); // Bind socket to a filename
// in the current directory.
// start polling for incoming packets
kernel->interrupt->Schedule(this, NetworkTime, NetworkRecvInt);
}
//-----------------------------------------------------------------------
// NetworkInput::NetworkInput
// Deallocate the simulation for the network input
// (basically, deallocate the input mailbox)
//-----------------------------------------------------------------------
NetworkInput::~NetworkInput()
{
CloseSocket(sock);
DeAssignNameToSocket(sockName);
}
//-----------------------------------------------------------------------
// NetworkInput::CallBack
// Simulator calls this when a packet may be available to
// be read in from the simulated network.
//
// First check to make sure packet is available & there's space to
// pull it in. Then invoke the "callBack" registered by whoever
// wants the packet.
//-----------------------------------------------------------------------
void
NetworkInput::CallBack()
{
// schedule the next time to poll for a packet
kernel->interrupt->Schedule(this, NetworkTime, NetworkRecvInt);
if (inHdr.length != 0) // do nothing if packet is already buffered
return;
if (!PollSocket(sock)) // do nothing if no packet to be read
return;
// otherwise, read packet in
char *buffer = new char[MaxWireSize];
ReadFromSocket(sock, buffer, MaxWireSize);
// divide packet into header and data
inHdr = *(PacketHeader *)buffer;
ASSERT((inHdr.to == kernel->hostName) && (inHdr.length <= MaxPacketSize));
bcopy(buffer + sizeof(PacketHeader), inbox, inHdr.length);
delete [] buffer ;
DEBUG(dbgNet, "Network received packet from " << inHdr.from << ", length " << inHdr.length);
kernel->stats->numPacketsRecvd++;
// tell post office that the packet has arrived
callWhenAvail->CallBack();
}
//-----------------------------------------------------------------------
// NetworkInput::Receive
// Read a packet, if one is buffered
//-----------------------------------------------------------------------
PacketHeader
NetworkInput::Receive(char* data)
{
PacketHeader hdr = inHdr;
inHdr.length = 0;
if (hdr.length != 0) {
bcopy(inbox, data, hdr.length);
}
return hdr;
}
//-----------------------------------------------------------------------
// NetworkOutput::NetworkOutput
// Initialize the simulation for sending network packets
//
// "reliability" says whether we drop packets to emulate unreliable links
// "toCall" is the interrupt handler to call when next packet can be sent
//-----------------------------------------------------------------------
NetworkOutput::NetworkOutput(double reliability, CallBackObj *toCall)
{
if (reliability < 0) chanceToWork = 0;
else if (reliability > 1) chanceToWork = 1;
else chanceToWork = reliability;
// set up the stuff to emulate asynchronous interrupts
callWhenDone = toCall;
sendBusy = FALSE;
sock = OpenSocket();
}
//-----------------------------------------------------------------------
// NetworkOutput::~NetworkOutput
// Deallocate the simulation for sending network packets
//-----------------------------------------------------------------------
NetworkOutput::~NetworkOutput()
{
CloseSocket(sock);
}
//-----------------------------------------------------------------------
// NetworkOutput::CallBack
// Called by simulator when another packet can be sent.
//-----------------------------------------------------------------------
void
NetworkOutput::CallBack()
{
sendBusy = FALSE;
kernel->stats->numPacketsSent++;
callWhenDone->CallBack();
}
//-----------------------------------------------------------------------
// NetworkOutput::Send
// Send a packet into the simulated network, to the destination in hdr.
// Concatenate hdr and data, and schedule an interrupt to tell the user
// when the next packet can be sent
//
// Note we always pad out a packet to MaxWireSize before putting it into
// the socket, because it's simpler at the receive end.
//-----------------------------------------------------------------------
void
NetworkOutput::Send(PacketHeader hdr, char* data)
{
char toName[32];
sprintf(toName, "SOCKET_%d", (int)hdr.to);
ASSERT((sendBusy == FALSE) && (hdr.length > 0) &&
(hdr.length <= MaxPacketSize) && (hdr.from == kernel->hostName));
DEBUG(dbgNet, "Sending to addr " << hdr.to << ", length " << hdr.length);
kernel->interrupt->Schedule(this, NetworkTime, NetworkSendInt);
if (RandomNumber() % 100 >= chanceToWork * 100) { // emulate a lost packet
DEBUG(dbgNet, "oops, lost it!");
return;
}
// concatenate hdr and data into a single buffer, and send it out
char *buffer = new char[MaxWireSize];
*(PacketHeader *)buffer = hdr;
bcopy(data, buffer + sizeof(PacketHeader), hdr.length);
SendToSocket(sock, buffer, MaxWireSize, toName);
delete [] buffer;
}

110
code/machine/network.h Executable file
View File

@@ -0,0 +1,110 @@
// network.h
// Data structures to emulate a physical network connection.
// The network provides the abstraction of ordered, unreliable,
// fixed-size packet delivery to other machines on the network.
//
// You may note that the interface to the network is similar to
// the console device -- both are full duplex channels.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef NETWORK_H
#define NETWORK_H
#include "copyright.h"
#include "utility.h"
#include "callback.h"
// Network address -- uniquely identifies a machine. This machine's ID
// is given on the command line.
typedef int NetworkAddress;
// The following class defines the network packet header.
// The packet header is prepended to the data payload by the Network driver,
// before the packet is sent over the wire. The format on the wire is:
// packet header (PacketHeader)
// data (containing MailHeader from the PostOffice!)
class PacketHeader {
public:
NetworkAddress to; // Destination machine ID
NetworkAddress from; // source machine ID
unsigned length; // bytes of packet data, excluding the
// packet header (but including the
// MailHeader prepended by the post office)
};
#define MaxWireSize 64 // largest packet that can go out on the wire
#define MaxPacketSize (MaxWireSize - sizeof(struct PacketHeader))
// data "payload" of the largest packet
// The following two classes defines a physical network device. The network
// is capable of delivering fixed sized packets, in order but unreliably,
// to other machines connected to the network.
//
// The "reliability" of the network can be specified to the constructor.
// This number, between 0 and 1, is the chance that the network will lose
// a packet. Note that you can change the seed for the random number
// generator, by changing the arguments to RandomInit() in Initialize().
// The random number generator is used to choose which packets to drop.
class NetworkInput : public CallBackObj{
public:
NetworkInput(CallBackObj *toCall);
// Allocate and initialize network input driver
~NetworkInput(); // De-allocate the network input driver data
PacketHeader Receive(char* data);
// Poll the network for incoming messages.
// If there is a packet waiting, copy the
// packet into "data" and return the header.
// If no packet is waiting, return a header
// with length 0.
void CallBack(); // A packet may have arrived.
private:
int sock; // UNIX socket number for incoming packets
char sockName[32]; // File name corresponding to UNIX socket
CallBackObj *callWhenAvail; // Interrupt handler, signalling packet has
// arrived.
bool packetAvail; // Packet has arrived, can be pulled off of
// network
PacketHeader inHdr; // Information about arrived packet
char inbox[MaxPacketSize]; // Data for arrived packet
};
class NetworkOutput : public CallBackObj {
public:
NetworkOutput(double reliability, CallBackObj *toCall);
// Allocate and initialize network output driver
~NetworkOutput(); // De-allocate the network input driver data
void Send(PacketHeader hdr, char* data);
// Send the packet data to a remote machine,
// specified by "hdr". Returns immediately.
// "callWhenDone" is invoked once the next
// packet can be sent. Note that callWhenDone
// is called whether or not the packet is
// dropped, and note that the "from" field of
// the PacketHeader is filled in automatically
// by Send().
void CallBack(); // Interrupt handler, called when message is
// sent
private:
int sock; // UNIX socket number for outgoing packets
double chanceToWork; // Likelihood packet will be dropped
CallBackObj *callWhenDone; // Interrupt handler, signalling next packet
// can be sent.
bool sendBusy; // Packet is being sent.
};
#endif // NETWORK_H

45
code/machine/stats.cc Executable file
View File

@@ -0,0 +1,45 @@
// stats.h
// Routines for managing statistics about Nachos performance.
//
// DO NOT CHANGE -- these stats are maintained by the machine emulation.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "debug.h"
#include "stats.h"
//----------------------------------------------------------------------
// Statistics::Statistics
// Initialize performance metrics to zero, at system startup.
//----------------------------------------------------------------------
Statistics::Statistics()
{
totalTicks = idleTicks = systemTicks = userTicks = 0;
numDiskReads = numDiskWrites = 0;
numConsoleCharsRead = numConsoleCharsWritten = 0;
numPageFaults = numPacketsSent = numPacketsRecvd = 0;
}
//----------------------------------------------------------------------
// Statistics::Print
// Print performance metrics, when we've finished everything
// at system shutdown.
//----------------------------------------------------------------------
void
Statistics::Print()
{
cout << "Ticks: total " << totalTicks << ", idle " << idleTicks;
cout << ", system " << systemTicks << ", user " << userTicks <<"\n";
cout << "Disk I/O: reads " << numDiskReads;
cout << ", writes " << numDiskWrites << "\n";
cout << "Console I/O: reads " << numConsoleCharsRead;
cout << ", writes " << numConsoleCharsWritten << "\n";
cout << "Paging: faults " << numPageFaults << "\n";
cout << "Network I/O: packets received " << numPacketsRecvd;
cout << ", sent " << numPacketsSent << "\n";
}

60
code/machine/stats.h Executable file
View File

@@ -0,0 +1,60 @@
// stats.h
// Data structures for gathering statistics about Nachos performance.
//
// DO NOT CHANGE -- these stats are maintained by the machine emulation
//
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef STATS_H
#define STATS_H
#include "copyright.h"
// The following class defines the statistics that are to be kept
// about Nachos behavior -- how much time (ticks) elapsed, how
// many user instructions executed, etc.
//
// The fields in this class are public to make it easier to update.
class Statistics {
public:
int totalTicks; // Total time running Nachos
int idleTicks; // Time spent idle (no threads to run)
int systemTicks; // Time spent executing system code
int userTicks; // Time spent executing user code
// (this is also equal to # of
// user instructions executed)
int numDiskReads; // number of disk read requests
int numDiskWrites; // number of disk write requests
int numConsoleCharsRead; // number of characters read from the keyboard
int numConsoleCharsWritten; // number of characters written to the display
int numPageFaults; // number of virtual memory page faults
int numPacketsSent; // number of packets sent over the network
int numPacketsRecvd; // number of packets received over the network
Statistics(); // initialize everything to zero
void Print(); // print collected statistics
};
// Constants used to reflect the relative time an operation would
// take in a real system. A "tick" is a just a unit of time -- if you
// like, a microsecond.
//
// Since Nachos kernel code is directly executed, and the time spent
// in the kernel measured by the number of calls to enable interrupts,
// these time constants are none too exact.
const int UserTick = 1; // advance for each user-level instruction
const int SystemTick = 10; // advance each time interrupts are enabled
const int RotationTime = 500; // time disk takes to rotate one sector
const int SeekTime = 500; // time disk takes to seek past one track
const int ConsoleTime = 100; // time to read or write one character
const int NetworkTime = 100; // time to send or receive one packet
const int TimerTicks = 100; // (average) time between timer interrupts
#endif // STATS_H

81
code/machine/timer.cc Executable file
View File

@@ -0,0 +1,81 @@
// timer.cc
// Routines to emulate a hardware timer device.
//
// A hardware timer generates a CPU interrupt every X milliseconds.
// This means it can be used for implementing time-slicing.
//
// We emulate a hardware timer by scheduling an interrupt to occur
// every time stats->totalTicks has increased by TimerTicks.
//
// In order to introduce some randomness into time-slicing, if "doRandom"
// is set, then the interrupt is comes after a random number of ticks.
//
// Remember -- nothing in here is part of Nachos. It is just
// an emulation for the hardware that Nachos is running on top of.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "timer.h"
#include "main.h"
#include "sysdep.h"
//----------------------------------------------------------------------
// Timer::Timer
// Initialize a hardware timer device. Save the place to call
// on each interrupt, and then arrange for the timer to start
// generating interrupts.
//
// "doRandom" -- if true, arrange for the interrupts to occur
// at random, instead of fixed, intervals.
// "toCall" is the interrupt handler to call when the timer expires.
//----------------------------------------------------------------------
Timer::Timer(bool doRandom, CallBackObj *toCall)
{
randomize = doRandom;
callPeriodically = toCall;
disable = FALSE;
SetInterrupt();
}
//----------------------------------------------------------------------
// Timer::CallBack
// Routine called when interrupt is generated by the hardware
// timer device. Schedule the next interrupt, and invoke the
// interrupt handler.
//----------------------------------------------------------------------
void
Timer::CallBack()
{
// invoke the Nachos interrupt handler for this device
callPeriodically->CallBack();
SetInterrupt(); // do last, to let software interrupt handler
// decide if it wants to disable future interrupts
}
//----------------------------------------------------------------------
// Timer::SetInterrupt
// Cause a timer interrupt to occur in the future, unless
// future interrupts have been disabled. The delay is either
// fixed or random.
//----------------------------------------------------------------------
void
Timer::SetInterrupt()
{
if (!disable) {
int delay = TimerTicks;
if (randomize) {
delay = 1 + (RandomNumber() % (TimerTicks * 2));
}
// schedule the next timer device interrupt
kernel->interrupt->Schedule(this, delay, TimerInt);
}
}

53
code/machine/timer.h Executable file
View File

@@ -0,0 +1,53 @@
// timer.h
// Data structures to emulate a hardware timer.
//
// A hardware timer generates a CPU interrupt every X milliseconds.
// This means it can be used for implementing time-slicing, or for
// having a thread go to sleep for a specific period of time.
//
// We emulate a hardware timer by scheduling an interrupt to occur
// every time stats->totalTicks has increased by TimerTicks.
//
// In order to introduce some randomness into time-slicing, if "doRandom"
// is set, then the interrupt comes after a random number of ticks.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef TIMER_H
#define TIMER_H
#include "copyright.h"
#include "utility.h"
#include "callback.h"
// The following class defines a hardware timer.
class Timer : public CallBackObj {
public:
Timer(bool doRandom, CallBackObj *toCall);
// Initialize the timer, and callback to "toCall"
// every time slice.
virtual ~Timer() {}
void Disable() { disable = TRUE; }
// Turn timer device off, so it doesn't
// generate any more interrupts.
private:
bool randomize; // set if we need to use a random timeout delay
CallBackObj *callPeriodically; // call this every TimerTicks time units
bool disable; // turn off the timer device after next
// interrupt.
void CallBack(); // called internally when the hardware
// timer generates an interrupt
void SetInterrupt(); // cause an interrupt to occur in the
// the future after a fixed or random
// delay
};
#endif // TIMER_H

250
code/machine/translate.cc Executable file
View File

@@ -0,0 +1,250 @@
// translate.cc
// Routines to translate virtual addresses to physical addresses.
// Software sets up a table of legal translations. We look up
// in the table on every memory reference to find the true physical
// memory location.
//
// Two types of translation are supported here.
//
// Linear page table -- the virtual page # is used as an index
// into the table, to find the physical page #.
//
// Translation lookaside buffer -- associative lookup in the table
// to find an entry with the same virtual page #. If found,
// this entry is used for the translation.
// If not, it traps to software with an exception.
//
// In practice, the TLB is much smaller than the amount of physical
// memory (16 entries is common on a machine that has 1000's of
// pages). Thus, there must also be a backup translation scheme
// (such as page tables), but the hardware doesn't need to know
// anything at all about that.
//
// Note that the contents of the TLB are specific to an address space.
// If the address space changes, so does the contents of the TLB!
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "main.h"
// Routines for converting Words and Short Words to and from the
// simulated machine's format of little endian. These end up
// being NOPs when the host machine is also little endian (DEC and Intel).
unsigned int
WordToHost(unsigned int word) {
#ifdef HOST_IS_BIG_ENDIAN
register unsigned long result;
result = (word >> 24) & 0x000000ff;
result |= (word >> 8) & 0x0000ff00;
result |= (word << 8) & 0x00ff0000;
result |= (word << 24) & 0xff000000;
return result;
#else
return word;
#endif /* HOST_IS_BIG_ENDIAN */
}
unsigned short
ShortToHost(unsigned short shortword) {
#ifdef HOST_IS_BIG_ENDIAN
register unsigned short result;
result = (shortword << 8) & 0xff00;
result |= (shortword >> 8) & 0x00ff;
return result;
#else
return shortword;
#endif /* HOST_IS_BIG_ENDIAN */
}
unsigned int
WordToMachine(unsigned int word) { return WordToHost(word); }
unsigned short
ShortToMachine(unsigned short shortword) { return ShortToHost(shortword); }
//----------------------------------------------------------------------
// Machine::ReadMem
// Read "size" (1, 2, or 4) bytes of virtual memory at "addr" into
// the location pointed to by "value".
//
// Returns FALSE if the translation step from virtual to physical memory
// failed.
//
// "addr" -- the virtual address to read from
// "size" -- the number of bytes to read (1, 2, or 4)
// "value" -- the place to write the result
//----------------------------------------------------------------------
bool
Machine::ReadMem(int addr, int size, int *value)
{
int data;
ExceptionType exception;
int physicalAddress;
DEBUG(dbgAddr, "Reading VA " << addr << ", size " << size);
exception = Translate(addr, &physicalAddress, size, FALSE);
if (exception != NoException) {
RaiseException(exception, addr);
return FALSE;
}
switch (size) {
case 1:
data = mainMemory[physicalAddress];
*value = data;
break;
case 2:
data = *(unsigned short *) &mainMemory[physicalAddress];
*value = ShortToHost(data);
break;
case 4:
data = *(unsigned int *) &mainMemory[physicalAddress];
*value = WordToHost(data);
break;
default: ASSERT(FALSE);
}
DEBUG(dbgAddr, "\tvalue read = " << *value);
return (TRUE);
}
//----------------------------------------------------------------------
// Machine::WriteMem
// Write "size" (1, 2, or 4) bytes of the contents of "value" into
// virtual memory at location "addr".
//
// Returns FALSE if the translation step from virtual to physical memory
// failed.
//
// "addr" -- the virtual address to write to
// "size" -- the number of bytes to be written (1, 2, or 4)
// "value" -- the data to be written
//----------------------------------------------------------------------
bool
Machine::WriteMem(int addr, int size, int value)
{
ExceptionType exception;
int physicalAddress;
DEBUG(dbgAddr, "Writing VA " << addr << ", size " << size << ", value " << value);
exception = Translate(addr, &physicalAddress, size, TRUE);
if (exception != NoException) {
RaiseException(exception, addr);
return FALSE;
}
switch (size) {
case 1:
mainMemory[physicalAddress] = (unsigned char) (value & 0xff);
break;
case 2:
*(unsigned short *) &mainMemory[physicalAddress]
= ShortToMachine((unsigned short) (value & 0xffff));
break;
case 4:
*(unsigned int *) &mainMemory[physicalAddress]
= WordToMachine((unsigned int) value);
break;
default: ASSERT(FALSE);
}
return TRUE;
}
//----------------------------------------------------------------------
// Machine::Translate
// Translate a virtual address into a physical address, using
// either a page table or a TLB. Check for alignment and all sorts
// of other errors, and if everything is ok, set the use/dirty bits in
// the translation table entry, and store the translated physical
// address in "physAddr". If there was an error, returns the type
// of the exception.
//
// "virtAddr" -- the virtual address to translate
// "physAddr" -- the place to store the physical address
// "size" -- the amount of memory being read or written
// "writing" -- if TRUE, check the "read-only" bit in the TLB
//----------------------------------------------------------------------
ExceptionType
Machine::Translate(int virtAddr, int* physAddr, int size, bool writing)
{
int i;
unsigned int vpn, offset;
TranslationEntry *entry;
unsigned int pageFrame;
DEBUG(dbgAddr, "\tTranslate " << virtAddr << (writing ? " , write" : " , read"));
// check for alignment errors
if (((size == 4) && (virtAddr & 0x3)) || ((size == 2) && (virtAddr & 0x1))){
DEBUG(dbgAddr, "Alignment problem at " << virtAddr << ", size " << size);
return AddressErrorException;
}
// we must have either a TLB or a page table, but not both!
ASSERT(tlb == NULL || pageTable == NULL);
ASSERT(tlb != NULL || pageTable != NULL);
// calculate the virtual page number, and offset within the page,
// from the virtual address
vpn = (unsigned) virtAddr / PageSize;
offset = (unsigned) virtAddr % PageSize;
if (tlb == NULL) { // => page table => vpn is index into table
if (vpn >= pageTableSize) {
DEBUG(dbgAddr, "Illegal virtual page # " << virtAddr);
return AddressErrorException;
} else if (!pageTable[vpn].valid) {
DEBUG(dbgAddr, "Invalid virtual page # " << virtAddr);
return PageFaultException;
}
entry = &pageTable[vpn];
} else {
for (entry = NULL, i = 0; i < TLBSize; i++)
if (tlb[i].valid && (tlb[i].virtualPage == ((int)vpn))) {
entry = &tlb[i]; // FOUND!
break;
}
if (entry == NULL) { // not found
DEBUG(dbgAddr, "Invalid TLB entry for this virtual page!");
return PageFaultException; // really, this is a TLB fault,
// the page may be in memory,
// but not in the TLB
}
}
if (entry->readOnly && writing) { // trying to write to a read-only page
DEBUG(dbgAddr, "Write to read-only page at " << virtAddr);
return ReadOnlyException;
}
pageFrame = entry->physicalPage;
// if the pageFrame is too big, there is something really wrong!
// An invalid translation was loaded into the page table or TLB.
if (pageFrame >= NumPhysPages) {
DEBUG(dbgAddr, "Illegal pageframe " << pageFrame);
return BusErrorException;
}
entry->use = TRUE; // set the use, dirty bits
if (writing)
entry->dirty = TRUE;
*physAddr = pageFrame * PageSize + offset;
ASSERT((*physAddr >= 0) && ((*physAddr + size) <= MemorySize));
DEBUG(dbgAddr, "phys addr = " << *physAddr);
return NoException;
}

45
code/machine/translate.h Executable file
View File

@@ -0,0 +1,45 @@
// translate.h
// Data structures for managing the translation from
// virtual page # -> physical page #, used for managing
// physical memory on behalf of user programs.
//
// The data structures in this file are "dual-use" - they
// serve both as a page table entry, and as an entry in
// a software-managed translation lookaside buffer (TLB).
// Either way, each entry is of the form:
// <virtual page #, physical page #>.
//
// DO NOT CHANGE -- part of the machine emulation
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef TLB_H
#define TLB_H
#include "copyright.h"
#include "utility.h"
// The following class defines an entry in a translation table -- either
// in a page table or a TLB. Each entry defines a mapping from one
// virtual page to one physical page.
// In addition, there are some extra bits for access control (valid and
// read-only) and some bits for usage information (use and dirty).
class TranslationEntry {
public:
int virtualPage; // The page number in virtual memory.
int physicalPage; // The page number in real memory (relative to the
// start of "mainMemory"
bool valid; // If this bit is set, the translation is ignored.
// (In other words, the entry hasn't been initialized.)
bool readOnly; // If this bit is set, the user program is not allowed
// to modify the contents of the page.
bool use; // This bit is set by the hardware every time the
// page is referenced or modified.
bool dirty; // This bit is set by the hardware every time the
// page is modified.
};
#endif

344
code/network/post.cc Executable file
View File

@@ -0,0 +1,344 @@
// post.cc
// Routines to deliver incoming network messages to the correct
// "address" -- a mailbox, or a holding area for incoming messages.
// This module operates just like the US postal service (in other
// words, it works, but it's slow, and you can't really be sure if
// your mail really got through!).
//
// Note that once we prepend the MailHdr to the outgoing message data,
// the combination (MailHdr plus data) looks like "data" to the Network
// device.
//
// The implementation synchronizes incoming messages with threads
// waiting for those messages.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "post.h"
//----------------------------------------------------------------------
// Mail::Mail
// Initialize a single mail message, by concatenating the headers to
// the data.
//
// "pktH" -- source, destination machine ID's
// "mailH" -- source, destination mailbox ID's
// "data" -- payload data
//----------------------------------------------------------------------
Mail::Mail(PacketHeader pktH, MailHeader mailH, char *msgData)
{
ASSERT(mailH.length <= MaxMailSize);
pktHdr = pktH;
mailHdr = mailH;
bcopy(msgData, data, mailHdr.length);
}
//----------------------------------------------------------------------
// MailBox::MailBox
// Initialize a single mail box within the post office, so that it
// can receive incoming messages.
//
// Just initialize a list of messages, representing the mailbox.
//----------------------------------------------------------------------
MailBox::MailBox()
{
messages = new SynchList<Mail *>();
}
//----------------------------------------------------------------------
// MailBox::~MailBox
// De-allocate a single mail box within the post office.
//
// Just delete the mailbox, and throw away all the queued messages
// in the mailbox.
//----------------------------------------------------------------------
MailBox::~MailBox()
{
delete messages;
}
//----------------------------------------------------------------------
// PrintHeader
// Print the message header -- the destination machine ID and mailbox
// #, source machine ID and mailbox #, and message length.
//
// "pktHdr" -- source, destination machine ID's
// "mailHdr" -- source, destination mailbox ID's
//----------------------------------------------------------------------
static void
PrintHeader(PacketHeader pktHdr, MailHeader mailHdr)
{
cout << "From (" << pktHdr.from << ", " << mailHdr.from << ") to (" <<
pktHdr.to << ", " << mailHdr.to << ") bytes " << mailHdr.length << "\n";
}
//----------------------------------------------------------------------
// MailBox::Put
// Add a message to the mailbox. If anyone is waiting for message
// arrival, wake them up!
//
// We need to reconstruct the Mail message (by concatenating the headers
// to the data), to simplify queueing the message on the SynchList.
//
// "pktHdr" -- source, destination machine ID's
// "mailHdr" -- source, destination mailbox ID's
// "data" -- payload message data
//----------------------------------------------------------------------
void
MailBox::Put(PacketHeader pktHdr, MailHeader mailHdr, char *data)
{
Mail *mail = new Mail(pktHdr, mailHdr, data);
messages->Append(mail); // put on the end of the list of
// arrived messages, and wake up
// any waiters
}
//----------------------------------------------------------------------
// MailBox::Get
// Get a message from a mailbox, parsing it into the packet header,
// mailbox header, and data.
//
// The calling thread waits if there are no messages in the mailbox.
//
// "pktHdr" -- address to put: source, destination machine ID's
// "mailHdr" -- address to put: source, destination mailbox ID's
// "data" -- address to put: payload message data
//----------------------------------------------------------------------
void
MailBox::Get(PacketHeader *pktHdr, MailHeader *mailHdr, char *data)
{
DEBUG(dbgNet, "Waiting for mail in mailbox");
Mail *mail = messages->RemoveFront(); // remove message from list;
// will wait if list is empty
*pktHdr = mail->pktHdr;
*mailHdr = mail->mailHdr;
if (debug->IsEnabled('n')) {
cout << "Got mail from mailbox: ";
PrintHeader(*pktHdr, *mailHdr);
}
bcopy(mail->data, data, mail->mailHdr.length);
// copy the message data into
// the caller's buffer
delete mail; // we've copied out the stuff we
// need, we can now discard the message
}
//----------------------------------------------------------------------
// PostOfficeInput::PostOfficeInput
// Initialize the post office input queues as a collection of mailboxes.
// Also initialize the network device, to allow post offices
// on different machines to deliver messages to one another.
//
// We use a separate thread "the postal worker" to wait for messages
// to arrive, and deliver them to the correct mailbox. Note that
// delivering messages to the mailboxes can't be done directly
// by the interrupt handlers, because it requires a Lock.
//
// "nBoxes" is the number of mail boxes in this Post Office
//----------------------------------------------------------------------
PostOfficeInput::PostOfficeInput(int nBoxes)
{
messageAvailable = new Semaphore("message available", 0);
numBoxes = nBoxes;
boxes = new MailBox[nBoxes];
network = new NetworkInput(this);
Thread *t = new Thread("postal worker", 1);
t->Fork(PostOfficeInput::PostalDelivery, this);
}
//----------------------------------------------------------------------
// PostOfficeInput::~PostOfficeInput
// De-allocate the post office data structures.
//
// Since the postal helper is waiting on the "messageAvail" semaphore,
// we don't deallocate it! This leaves garbage lying about,
// but the alternative is worse!
//----------------------------------------------------------------------
PostOfficeInput::~PostOfficeInput()
{
delete network;
delete [] boxes;
}
//----------------------------------------------------------------------
// PostOffice::PostalDelivery
// Wait for incoming messages, and put them in the right mailbox.
//
// Incoming messages have had the PacketHeader stripped off,
// but the MailHeader is still tacked on the front of the data.
//----------------------------------------------------------------------
void
PostOfficeInput::PostalDelivery(void* data)
{
PostOfficeInput* _this = (PostOfficeInput*)data;
PacketHeader pktHdr;
MailHeader mailHdr;
char *buffer = new char[MaxPacketSize];
for (;;) {
// first, wait for a message
_this->messageAvailable->P();
pktHdr = _this->network->Receive(buffer);
mailHdr = *(MailHeader *)buffer;
if (debug->IsEnabled('n')) {
cout << "Putting mail into mailbox: ";
PrintHeader(pktHdr, mailHdr);
}
// check that arriving message is legal!
ASSERT(0 <= mailHdr.to && mailHdr.to < _this->numBoxes);
ASSERT(mailHdr.length <= MaxMailSize);
// put into mailbox
_this->boxes[mailHdr.to].Put(pktHdr, mailHdr, buffer + sizeof(MailHeader));
}
}
//----------------------------------------------------------------------
// PostOfficeInput::Receive
// Retrieve a message from a specific box if one is available,
// otherwise wait for a message to arrive in the box.
//
// Note that the MailHeader + data looks just like normal payload
// data to the Network.
//
//
// "box" -- mailbox ID in which to look for message
// "pktHdr" -- address to put: source, destination machine ID's
// "mailHdr" -- address to put: source, destination mailbox ID's
// "data" -- address to put: payload message data
//----------------------------------------------------------------------
void
PostOfficeInput::Receive(int box, PacketHeader *pktHdr,
MailHeader *mailHdr, char* data)
{
ASSERT((box >= 0) && (box < numBoxes));
boxes[box].Get(pktHdr, mailHdr, data);
ASSERT(mailHdr->length <= MaxMailSize);
}
//----------------------------------------------------------------------
// PostOffice::CallBack
// Interrupt handler, called when a packet arrives from the network.
//
// Signal the PostalDelivery routine that it is time to get to work!
//----------------------------------------------------------------------
void
PostOfficeInput::CallBack()
{
messageAvailable->V();
}
//----------------------------------------------------------------------
// PostOfficeOutput::PostOfficeOutput
// Initialize the post office output queue.
//
// "reliability" is the probability that a network packet will
// be delivered (e.g., reliability = 1 means the network never
// drops any packets; reliability = 0 means the network never
// delivers any packets)
//----------------------------------------------------------------------
PostOfficeOutput::PostOfficeOutput(double reliability)
{
messageSent = new Semaphore("message sent", 0);
sendLock = new Lock("message send lock");
network = new NetworkOutput(reliability, this);
}
//----------------------------------------------------------------------
// PostOfficeOutput::~PostOfficeOutput
// De-allocate the post office data structures.
//----------------------------------------------------------------------
PostOfficeOutput::~PostOfficeOutput()
{
delete network;
delete messageSent;
delete sendLock;
}
//----------------------------------------------------------------------
// PostOfficeOutput::Send
// Concatenate the MailHeader to the front of the data, and pass
// the result to the Network for delivery to the destination machine.
//
// Note that the MailHeader + data looks just like normal payload
// data to the Network.
//
// "pktHdr" -- source, destination machine ID's
// "mailHdr" -- source, destination mailbox ID's
// "data" -- payload message data
//----------------------------------------------------------------------
void
PostOfficeOutput::Send(PacketHeader pktHdr, MailHeader mailHdr, char* data)
{
char* buffer = new char[MaxPacketSize]; // space to hold concatenated
// mailHdr + data
if (debug->IsEnabled('n')) {
cout << "Post send: ";
PrintHeader(pktHdr, mailHdr);
}
ASSERT(mailHdr.length <= MaxMailSize);
ASSERT(0 <= mailHdr.to);
// fill in pktHdr, for the Network layer
pktHdr.from = kernel->hostName;
pktHdr.length = mailHdr.length + sizeof(MailHeader);
// concatenate MailHeader and data
bcopy((char *)&mailHdr, buffer, sizeof(MailHeader));
bcopy(data, buffer + sizeof(MailHeader), mailHdr.length);
sendLock->Acquire(); // only one message can be sent
// to the network at any one time
network->Send(pktHdr, buffer);
messageSent->P(); // wait for interrupt to tell us
// ok to send the next message
sendLock->Release();
delete [] buffer; // we've sent the message, so
// we can delete our buffer
}
//----------------------------------------------------------------------
// PostOfficeOutput::CallBack
// Interrupt handler, called when the next packet can be put onto the
// network.
//
// Called even if the previous packet was dropped.
//----------------------------------------------------------------------
void
PostOfficeOutput::CallBack()
{
messageSent->V();
}

151
code/network/post.h Executable file
View File

@@ -0,0 +1,151 @@
// post.h
// Data structures for providing the abstraction of unreliable,
// ordered, fixed-size message delivery to mailboxes on other
// (directly connected) machines. Messages can be dropped by
// the network, but they are never corrupted.
//
// The US Post Office (and Canada Post! -KMS)
// delivers mail to the addressed mailbox.
// By analogy, our post office delivers packets to a specific buffer
// (MailBox), based on the mailbox number stored in the packet header.
// Mail waits in the box until a thread asks for it; if the mailbox
// is empty, threads can wait for mail to arrive in it.
//
// Thus, the service our post office provides is to de-multiplex
// incoming packets, delivering them to the appropriate thread.
//
// With each message, you get a return address, which consists of a "from
// address", which is the id of the machine that sent the message, and
// a "from box", which is the number of a mailbox on the sending machine
// to which you can send an acknowledgement, if your protocol requires
// this.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef POST_H
#define POST_H
#include "copyright.h"
#include "utility.h"
#include "callback.h"
#include "network.h"
#include "synchlist.h"
#include "synch.h"
// Mailbox address -- uniquely identifies a mailbox on a given machine.
// A mailbox is just a place for temporary storage for messages.
typedef int MailBoxAddress;
// The following class defines part of the message header.
// This is prepended to the message by the PostOffice, before the message
// is sent to the Network.
class MailHeader {
public:
MailBoxAddress to; // Destination mail box
MailBoxAddress from; // Mail box to reply to
unsigned length; // Bytes of message data (excluding the
// mail header)
};
// Maximum "payload" -- real data -- that can included in a single message
// Excluding the MailHeader and the PacketHeader
#define MaxMailSize (MaxPacketSize - sizeof(MailHeader))
// The following class defines the format of an incoming/outgoing
// "Mail" message. The message format is layered:
// network header (PacketHeader)
// post office header (MailHeader)
// data
class Mail {
public:
Mail(PacketHeader pktH, MailHeader mailH, char *msgData);
// Initialize a mail message by
// concatenating the headers to the data
PacketHeader pktHdr; // Header appended by Network
MailHeader mailHdr; // Header appended by PostOffice
char data[MaxMailSize]; // Payload -- message data
};
// The following class defines a single mailbox, or temporary storage
// for messages. Incoming messages are put by the PostOffice into the
// appropriate mailbox, and these messages can then be retrieved by
// threads on this machine.
class MailBox {
public:
MailBox(); // Allocate and initialize mail box
~MailBox(); // De-allocate mail box
void Put(PacketHeader pktHdr, MailHeader mailHdr, char *data);
// Atomically put a message into the mailbox
void Get(PacketHeader *pktHdr, MailHeader *mailHdr, char *data);
// Atomically get a message out of the
// mailbox (and wait if there is no message
// to get!)
private:
SynchList<Mail *> *messages; // A mailbox is just a list of arrived messages
};
// The following two classes defines a "Post Office", or a collection of
// mailboxes. The Post Office provides two main operations:
// Send -- send a message to a mailbox on a remote machine
// Receive -- wait until a message is in the mailbox, then remove and
// return it.
//
// Incoming messages are put by the PostOffice into the
// appropriate mailbox, waking up any threads waiting on Receive.
class PostOfficeInput : public CallBackObj {
public:
PostOfficeInput(int nBoxes); // Allocate and initialize Post Office
~PostOfficeInput(); // De-allocate Post Office data
void Receive(int box, PacketHeader *pktHdr,
MailHeader *mailHdr, char *data);
// Retrieve a message from "box". Wait if
// there is no message in the box.
static void PostalDelivery(void* data);
// Wait for incoming messages,
// and then put them in the correct mailbox
void CallBack(); // Called when incoming packet has arrived
// and can be pulled off of network
// (i.e., time to call PostalDelivery)
private:
NetworkInput *network; // Physical network connection
MailBox *boxes; // Table of mail boxes to hold incoming mail
int numBoxes; // Number of mail boxes
Semaphore *messageAvailable;// V'ed when message has arrived from network
};
class PostOfficeOutput : public CallBackObj {
public:
PostOfficeOutput(double reliability);
// Allocate and initialize output
// "reliability" is how many packets
// get dropped by the underlying network
~PostOfficeOutput(); // De-allocate Post Office data
void Send(PacketHeader pktHdr, MailHeader mailHdr, char *data);
// Send a message to a mailbox on a remote
// machine. The fromBox in the MailHeader is
// the return box for ack's.
void CallBack(); // Called when outgoing packet has been
// put on network; next packet can now be sent
private:
NetworkOutput *network; // Physical network connection
Semaphore *messageSent; // V'ed when next message can be sent to network
Lock *sendLock; // Only one outgoing message at a time
};
#endif

12
code/test/AFS_students.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
make clean
make
../build.linux/nachos -e consoleIO_test1
echo "====================================="
../build.linux/nachos -e consoleIO_test2
echo "====================================="
../build.linux/nachos -e fileIO_test1
echo "====================================="
../build.linux/nachos -e fileIO_test2
echo "====================================="

BIN
code/test/DISK_0 Executable file

Binary file not shown.

165
code/test/Makefile Executable file
View File

@@ -0,0 +1,165 @@
#
# Makefile for building user programs to run on top of Nachos
#
# Use "make" to build the test executable(s)
# Use "make clean" to remove .o files and .coff files
# Use "make distclean" to remove all files produced by make, including
# the test executables
#
# This is a GNU Makefile. It must be used with the GNU make program.
# At UW, the GNU make program is /software/gnu/bin/make.
# In many other places it is known as "gmake".
# You may wish to include /software/gnu/bin/ early in your command
# search path, so that you will be using GNU make when you type "make".
#
# Several things to be aware of:
#
# It should not be necessary to build the test executables for
# every type of host machine on which Nachos runs. You should
# be able to build them once, and then use them regardless of
# the host machine type. That is because the test executables
# run on the simulated MIPS machine, and not on the host.
#
# However:
# (1) if you are experiencing problems with the test executables,
# it would be prudent to rebuild them on the host machine
# on which you are currently running Nachos. To do this,
# just type "make distclean", and then "make"
#
# (2) the procedure used to build the test executables does
# depend on the host machine you are on. All of the machine
# dependencies are isolated in the Makefile.dep file.
# It should be possible to build the test executables on
# any MFCF machine. In the MFCF environment, this makefile
# should automatically figure out what type of host you are
# on, and should use the appropriate procedure.
# However, if you are working outside the MFCF environment,
# you will need to build a cross-compiler, build coff2noff,
# and edit Makefile.dep in this directory before you
# can build the test programs.
#
# Nachos assumes that the location of the program startup routine (the
# location the kernel jumps to when the program initially starts up)
# is at location 0. This means: start.o must be the first .o passed
# to ld, in order for the routine "Start" to be loaded at location 0
#
# When you make the test programs, you will see messages like these:
# numsections 3
# Loading 3 sections:
# ".text", filepos 0xd0, mempos 0x0, size 0x440
# ".data", filepos 0x510, mempos 0x440, size 0x0
# ".bss", filepos 0x0, mempos 0x440, size 0x12c0
# These messages are normal. They come from the coff2noff program.
# They are useful in that they tell you how big the various parts of your
# compiled user program are, and where in the address space
# coff2noff is going to place them. This information is also
# recorded in the header of the executable file that coff2noff
# creates. See the method AddrSpace::Load (in userprog/addrspace.cc)
# for an example of how this header is used by the Nachos OS to set up the
# address space for a new process that will run the executable.
#
#
# Adding New Test Programs:
#
# You are free to write new test programs, and to modify the
# existing programs. If you write a new program, you will
# need to modify this makefile so that the new program will
# get built.
# You will need to make the following changes for each program
# you add:
# (1) add the program's name to PROGRAMS variable definition
# (2) add dependencies and build commands for the new
# program. The easiest way to do this is to
# copy the dependencies and commands for an
# existing program, and then change the names.
#
# For example, if you write a test program in foo.c, for which
# the executable is to be called foo, you should do the following:
#
# change the PROGRAMS definition to look like this:
#
# PROGRAMS = halt shell matmult sort foo
#
# add these dependencies/commands:
#
# foo.o: foo.c
# $(CC) $(CFLAGS) -c foo.c
# foo: foo.o start.o
# $(LD) $(LDFLAGS) start.o foo.o -o foo.coff
# $(COFF2NOFF) foo.coff foo
#
# Be careful when you copy the commands! The commands
# must be indented with a *TAB*, not a bunch of spaces.
#
#
#############################################################################
# Makefile.dep contains all machine-dependent definitions
# If you are trying to build coff2noff somewhere outside
# of the MFCF environment, you will almost certainly want
# to visit and edit Makefile.dep before doing so
#############################################################################
include Makefile.dep
CC = $(GCCDIR)gcc
AS = $(GCCDIR)as
LD = $(GCCDIR)ld
INCDIR =-I../userprog -I../lib
CFLAGS = -G 0 -c $(INCDIR) -B/usr/bin/local/nachos/lib/gcc-lib/decstation-ultrix/2.95.2/ -B/usr/bin/local/nachos/decstation-ultrix/bin/
ifeq ($(hosttype),unknown)
PROGRAMS = unknownhost
else
# change this if you create a new test program!
PROGRAMS = halt consoleIO_test1 consoleIO_test2 fileIO_test1 fileIO_test2
endif
all: $(PROGRAMS)
start.o: start.S ../userprog/syscall.h
$(CC) $(CFLAGS) $(ASFLAGS) -c start.S
halt.o: halt.c
$(CC) $(CFLAGS) -c halt.c
halt: halt.o start.o
$(LD) $(LDFLAGS) start.o halt.o -o halt.coff
$(COFF2NOFF) halt.coff halt
consoleIO_test1.o: consoleIO_test1.c
$(CC) $(CFLAGS) -c consoleIO_test1.c
consoleIO_test1: consoleIO_test1.o start.o
$(LD) $(LDFLAGS) start.o consoleIO_test1.o -o consoleIO_test1.coff
$(COFF2NOFF) consoleIO_test1.coff consoleIO_test1
consoleIO_test2.o: consoleIO_test2.c
$(CC) $(CFLAGS) -c consoleIO_test2.c
consoleIO_test2: consoleIO_test2.o start.o
$(LD) $(LDFLAGS) start.o consoleIO_test2.o -o consoleIO_test2.coff
$(COFF2NOFF) consoleIO_test2.coff consoleIO_test2
fileIO_test1.o: fileIO_test1.c
$(CC) $(CFLAGS) -c fileIO_test1.c
fileIO_test1: fileIO_test1.o start.o
$(LD) $(LDFLAGS) start.o fileIO_test1.o -o fileIO_test1.coff
$(COFF2NOFF) fileIO_test1.coff fileIO_test1
fileIO_test2.o: fileIO_test2.c
$(CC) $(CFLAGS) -c fileIO_test2.c
fileIO_test2: fileIO_test2.o start.o
$(LD) $(LDFLAGS) start.o fileIO_test2.o -o fileIO_test2.coff
$(COFF2NOFF) fileIO_test2.coff fileIO_test2
clean:
$(RM) -f *.o *.ii
$(RM) -f *.coff
distclean: clean
$(RM) -f $(PROGRAMS)
unknownhost:
@echo Host type could not be determined.
@echo make is terminating.
@echo If you are on an MFCF machine, contact the instructor to report this problem
@echo Otherwise, edit Makefile.dep and try again.

63
code/test/Makefile.dep Executable file
View File

@@ -0,0 +1,63 @@
#############################################################################
# Machine-specific definitions
#
# If you are not in the MFCF environment, you can either add a new
# automatic test for your machine/OS type, or you should set the
# necessary variables "manually" here
#############################################################################
# unfortunately, command line arguments to uname are not
# very consistent across UNIX flavours. However, the following
# seem to work almost everywhere in MFCF land
osname = $(shell uname -s)
osrelease = $(shell uname -r)
hosttype = unknown
# Test for x86 Linux
# !!! COMMENT THE FOLLOWING LINES OUT IF BUILDING FOR SOLARIS HOST !!!
# !!! ADD PATH TO CPP and CROSS COMPILER
ifeq ($(osname),Linux)
# full path name of your cpp program i.e.:
CPP = ../../usr/local/nachos/lib/gcc-lib/decstation-ultrix/2.95.2/cpp
# directory in which your gcc cross-compiler lives i.e.:
GCCDIR = ../../usr/local/nachos/bin/decstation-ultrix-
LDFLAGS = -T script -N
ASFLAGS = -mips2
CPPFLAGS = $(INCDIR)
COFF2NOFF = ../../coff2noff/coff2noff.x86Linux
hosttype = x86Linux
endif
ifeq ($(osname),Windows)
CPP = /usr/local/nachosxdev/lib/gcc-lib/decstation-ultrix/2.95.3/cpp0
# directory in which your gcc cross-compiler lives i.e.:
GCCDIR = /usr/local/nachosxdev/bin/decstation-ultrix-
LDFLAGS = -T script -N
ASFLAGS = -mips2
CPPFLAGS = $(INCDIR)
COFF2NOFF = ../../coff2noff/coff2noff.Windows
hosttype = Windows
endif
# Note:
# If you are trying to build on MacOS X
# try something like this, substituting whatever
# uname -s returns on your machine for the XXX
#
#ifeq ($(osname),XXX)
#CPP = full path name of your cpp program
#GCCDIR = directory in which your gcc cross-compiler lives
#LDFLAGS = -T script -N
#ASFLAGS = -mips2
#CPPFLAGS = $(INCDIR)
#COFF2NOFF = full pathname of your coff2noff program
# Note: it has been moved to part of the Nachos distribution
# COFF2NOFF = ../../coff2noff.mipsUltrix
#hosttype = MacOS
#endif

7
code/test/compile.sh Executable file
View File

@@ -0,0 +1,7 @@
cd ../build.linux
make depend
make clean
make
cd ../test
make clean
make

12
code/test/consoleIO_test1.c Executable file
View File

@@ -0,0 +1,12 @@
#include "syscall.h"
int
main()
{
int n;
for (n=9;n>5;n--) {
PrintInt(n);
}
Halt();
}

13
code/test/consoleIO_test2.c Executable file
View File

@@ -0,0 +1,13 @@
#include "syscall.h"
int
main()
{
int n;
for (n=15;n<=19;n++){
PrintInt(n);
}
Halt();
}

20
code/test/fileIO_test1.c Executable file
View File

@@ -0,0 +1,20 @@
#include "syscall.h"
int main(void)
{
char test[] = "abcdefghijklmnopqrstuvwxyz";
int success = Create("file1.test");
OpenFileId fid;
int i;
if (success != 1) MSG("Failed on creating file");
fid = Open("file1.test");
if (fid <= 0) MSG("Failed on opening file");
for (i = 0; i < 26; ++i) {
int count = Write(test + i, 1, fid);
if (count != 1) MSG("Failed on writing file");
}
success = Close(fid);
if (success != 1) MSG("Failed on closing file");
Halt();
}

22
code/test/fileIO_test2.c Executable file
View File

@@ -0,0 +1,22 @@
#include "syscall.h"
int main(void)
{
// you should run fileIO_test1 first before running this one
char test[26];
char check[] = "abcdefghijklmnopqrstuvwxyz";
OpenFileId fid;
int count, success, i;
fid = Open("file1.test");
if (fid <= 0) MSG("Failed on opening file");
count = Read(test, 26, fid);
if (count != 26) MSG("Failed on reading file");
success = Close(fid);
if (success != 1) MSG("Failed on closing file");
for (i = 0; i < 26; ++i) {
if (test[i] != check[i]) MSG("Failed: reading wrong result");
}
MSG("Passed! ^_^");
Halt();
}

20
code/test/halt.c Executable file
View File

@@ -0,0 +1,20 @@
/* halt.c
* Simple program to test whether running a user program works.
*
* Just do a "syscall" that shuts down the OS.
*
* NOTE: for some reason, user programs with global data structures
* sometimes haven't worked in the Nachos environment. So be careful
* out there! One option is to allocate data structures as
* automatics within a procedure, but if you do this, you have to
* be careful to allocate a big enough stack to hold the automatics!
*/
#include "syscall.h"
int
main()
{
Halt();
/* not reached */
}

36
code/test/script Executable file
View File

@@ -0,0 +1,36 @@
OUTPUT_FORMAT("ecoff-littlemips")
ENTRY(__start)
SECTIONS
{
.text 0 : {
_ftext = . ;
*(.init)
eprol = .;
*(.text)
*(.fini)
etext = .;
_etext = .;
}
.rdata . : {
*(.rdata)
}
_fdata = .;
.data . : {
*(.data)
CONSTRUCTORS
}
edata = .;
_edata = .;
_fbss = .;
.sbss . : {
*(.sbss)
*(.scommon)
}
.bss . : {
*(.bss)
*(COMMON)
}
end = .;
_end = .;
}

196
code/test/start.S Executable file
View File

@@ -0,0 +1,196 @@
/* Start.s
* Assembly language assist for user programs running on top of Nachos.
*
* Since we don't want to pull in the entire C library, we define
* what we need for a user program here, namely Start and the system
* calls.
*/
#define IN_ASM
#include "syscall.h"
.text
.align 2
/* -------------------------------------------------------------
* __start
* Initialize running a C program, by calling "main".
*
* NOTE: This has to be first, so that it gets loaded at location 0.
* The Nachos kernel always starts a program by jumping to location 0.
* -------------------------------------------------------------
*/
.globl __start
.ent __start
__start:
jal main
move $4,$0
jal Exit /* if we return from main, exit(0) */
.end __start
/* -------------------------------------------------------------
* System call stubs:
* Assembly language assist to make system calls to the Nachos kernel.
* There is one stub per system call, that places the code for the
* system call into register r2, and leaves the arguments to the
* system call alone (in other words, arg1 is in r4, arg2 is
* in r5, arg3 is in r6, arg4 is in r7)
*
* The return value is in r2. This follows the standard C calling
* convention on the MIPS.
* -------------------------------------------------------------
*/
.globl Halt
.ent Halt
Halt:
addiu $2,$0,SC_Halt
syscall
j $31
.end Halt
.globl MSG
.ent MSG
MSG:
addiu $2,$0,SC_MSG
syscall
j $31
.end MSG
.globl Add
.ent Add
Add:
addiu $2,$0,SC_Add
syscall
j $31
.end Add
.globl Exit
.ent Exit
Exit:
addiu $2,$0,SC_Exit
syscall
j $31
.end Exit
.globl Exec
.ent Exec
Exec:
addiu $2,$0,SC_Exec
syscall
j $31
.end Exec
.globl ExecV
.ent ExecV
ExecV:
addiu $2,$0,SC_ExecV
syscall
j $31
.end ExecV
.globl Join
.ent Join
Join:
addiu $2,$0,SC_Join
syscall
j $31
.end Join
.globl Create
.ent Create
Create:
addiu $2,$0,SC_Create
syscall
j $31
.end Create
.globl Remove
.ent Remove
Remove:
addiu $2,$0,SC_Remove
syscall
j $31
.end Remove
.globl Open
.ent Open
Open:
addiu $2,$0,SC_Open
syscall
j $31
.end Open
.globl Read
.ent Read
Read:
addiu $2,$0,SC_Read
syscall
j $31
.end Read
.globl Write
.ent Write
Write:
addiu $2,$0,SC_Write
syscall
j $31
.end Write
.globl Close
.ent Close
Close:
addiu $2,$0,SC_Close
syscall
j $31
.end Close
.globl Seek
.ent Seek
Seek:
addiu $2,$0,SC_Seek
syscall
j $31
.end Seek
.globl ThreadFork
.ent ThreadFork
ThreadFork:
addiu $2,$0,SC_ThreadFork
syscall
j $31
.end ThreadFork
.globl ThreadYield
.ent ThreadYield
ThreadYield:
addiu $2,$0,SC_ThreadYield
syscall
j $31
.end ThreadYield
.globl ThreadExit
.ent ThreadExit
ThreadExit:
addiu $2, $0, SC_ThreadExit
syscall
j $31
.end ThreadExit
.globl ThreadJoin
.ent ThreadJoin
ThreadJoin:
addiu $2, $0, SC_ThreadJoin
syscall
j $31
.end ThreadJoin
/* dummy function to keep gcc happy */
.globl __main
.ent __main
__main:
j $31
.end __main

55
code/threads/alarm.cc Executable file
View File

@@ -0,0 +1,55 @@
// alarm.cc
// Routines to use a hardware timer device to provide a
// software alarm clock. For now, we just provide time-slicing.
//
// Not completely implemented.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "alarm.h"
#include "main.h"
//----------------------------------------------------------------------
// Alarm::Alarm
// Initialize a software alarm clock. Start up a timer device
//
// "doRandom" -- if true, arrange for the hardware interrupts to
// occur at random, instead of fixed, intervals.
//----------------------------------------------------------------------
Alarm::Alarm(bool doRandom)
{
timer = new Timer(doRandom, this);
}
//----------------------------------------------------------------------
// Alarm::CallBack
// Software interrupt handler for the timer device. The timer device is
// set up to interrupt the CPU periodically (once every TimerTicks).
// This routine is called each time there is a timer interrupt,
// with interrupts disabled.
//
// Note that instead of calling Yield() directly (which would
// suspend the interrupt handler, not the interrupted thread
// which is what we wanted to context switch), we set a flag
// so that once the interrupt handler is done, it will appear as
// if the interrupted thread called Yield at the point it is
// was interrupted.
//
// For now, just provide time-slicing. Only need to time slice
// if we're currently running something (in other words, not idle).
//----------------------------------------------------------------------
void
Alarm::CallBack()
{
Interrupt *interrupt = kernel->interrupt;
MachineStatus status = interrupt->getStatus();
if (status != IdleMode) {
interrupt->YieldOnReturn();
}
}

42
code/threads/alarm.h Executable file
View File

@@ -0,0 +1,42 @@
// alarm.h
// Data structures for a software alarm clock.
//
// We make use of a hardware timer device, that generates
// an interrupt every X time ticks (on real systems, X is
// usually between 0.25 - 10 milliseconds).
//
// From this, we provide the ability for a thread to be
// woken up after a delay; we also provide time-slicing.
//
// NOTE: this abstraction is not completely implemented.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef ALARM_H
#define ALARM_H
#include "copyright.h"
#include "utility.h"
#include "callback.h"
#include "timer.h"
// The following class defines a software alarm clock.
class Alarm : public CallBackObj {
public:
Alarm(bool doRandomYield); // Initialize the timer, and callback
// to "toCall" every time slice.
~Alarm() { delete timer; }
void WaitUntil(int x); // suspend execution until time > now + x
// this method is not yet implemented
private:
Timer *timer; // the hardware timer device
void CallBack(); // called when the hardware
// timer generates an interrupt
};
#endif // ALARM_H

311
code/threads/kernel.cc Executable file
View File

@@ -0,0 +1,311 @@
// kernel.cc
// Initialization and cleanup routines for the Nachos kernel.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "debug.h"
#include "main.h"
#include "kernel.h"
#include "sysdep.h"
#include "synch.h"
#include "synchlist.h"
#include "libtest.h"
#include "string.h"
#include "synchdisk.h"
#include "post.h"
#include "synchconsole.h"
//----------------------------------------------------------------------
// Kernel::Kernel
// Interpret command line arguments in order to determine flags
// for the initialization (see also comments in main.cc)
//----------------------------------------------------------------------
Kernel::Kernel(int argc, char **argv)
{
randomSlice = FALSE;
debugUserProg = FALSE;
consoleIn = NULL; // default is stdin
consoleOut = NULL; // default is stdout
#ifndef FILESYS_STUB
formatFlag = FALSE;
#endif
reliability = 1; // network reliability, default is 1.0
hostName = 0; // machine id, also UNIX socket name
// 0 is the default machine id
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-rs") == 0) {
ASSERT(i + 1 < argc);
RandomInit(atoi(argv[i + 1]));// initialize pseudo-random
// number generator
randomSlice = TRUE;
i++;
} else if (strcmp(argv[i], "-s") == 0) {
debugUserProg = TRUE;
} else if (strcmp(argv[i], "-e") == 0) {
execfile[++execfileNum]= argv[++i];
cout << execfile[execfileNum] << endl;
} else if (strcmp(argv[i], "-ci") == 0) {
ASSERT(i + 1 < argc);
consoleIn = argv[i + 1];
i++;
} else if (strcmp(argv[i], "-co") == 0) {
ASSERT(i + 1 < argc);
consoleOut = argv[i + 1];
i++;
#ifndef FILESYS_STUB
} else if (strcmp(argv[i], "-f") == 0) {
formatFlag = TRUE;
#endif
} else if (strcmp(argv[i], "-n") == 0) {
ASSERT(i + 1 < argc); // next argument is float
reliability = atof(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "-m") == 0) {
ASSERT(i + 1 < argc); // next argument is int
hostName = atoi(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "-u") == 0) {
cout << "Partial usage: nachos [-rs randomSeed]\n";
cout << "Partial usage: nachos [-s]\n";
cout << "Partial usage: nachos [-ci consoleIn] [-co consoleOut]\n";
#ifndef FILESYS_STUB
cout << "Partial usage: nachos [-nf]\n";
#endif
cout << "Partial usage: nachos [-n #] [-m #]\n";
}
}
}
//----------------------------------------------------------------------
// Kernel::Initialize
// Initialize Nachos global data structures. Separate from the
// constructor because some of these refer to earlier initialized
// data via the "kernel" global variable.
//----------------------------------------------------------------------
void
Kernel::Initialize()
{
// We didn't explicitly allocate the current thread we are running in.
// But if it ever tries to give up the CPU, we better have a Thread
// object to save its state.
currentThread = new Thread("main", threadNum++);
currentThread->setStatus(RUNNING);
stats = new Statistics(); // collect statistics
interrupt = new Interrupt; // start up interrupt handling
scheduler = new Scheduler(); // initialize the ready queue
alarm = new Alarm(randomSlice); // start up time slicing
machine = new Machine(debugUserProg);
synchConsoleIn = new SynchConsoleInput(consoleIn); // input from stdin
synchConsoleOut = new SynchConsoleOutput(consoleOut); // output to stdout
synchDisk = new SynchDisk(); //
#ifdef FILESYS_STUB
fileSystem = new FileSystem();
#else
fileSystem = new FileSystem(formatFlag);
#endif // FILESYS_STUB
postOfficeIn = new PostOfficeInput(10);
postOfficeOut = new PostOfficeOutput(reliability);
interrupt->Enable();
}
//----------------------------------------------------------------------
// Kernel::~Kernel
// Nachos is halting. De-allocate global data structures.
//----------------------------------------------------------------------
Kernel::~Kernel()
{
delete stats;
delete interrupt;
delete scheduler;
delete alarm;
delete machine;
delete synchConsoleIn;
delete synchConsoleOut;
delete synchDisk;
delete fileSystem;
delete postOfficeIn;
delete postOfficeOut;
Exit(0);
}
//----------------------------------------------------------------------
// Kernel::ThreadSelfTest
// Test threads, semaphores, synchlists
//----------------------------------------------------------------------
void
Kernel::ThreadSelfTest() {
Semaphore *semaphore;
SynchList<int> *synchList;
LibSelfTest(); // test library routines
currentThread->SelfTest(); // test thread switching
// test semaphore operation
semaphore = new Semaphore("test", 0);
semaphore->SelfTest();
delete semaphore;
// test locks, condition variables
// using synchronized lists
synchList = new SynchList<int>;
synchList->SelfTest(9);
delete synchList;
}
//----------------------------------------------------------------------
// Kernel::ConsoleTest
// Test the synchconsole
//----------------------------------------------------------------------
void
Kernel::ConsoleTest() {
char ch;
cout << "Testing the console device.\n"
<< "Typed characters will be echoed, until ^D is typed.\n"
<< "Note newlines are needed to flush input through UNIX.\n";
cout.flush();
do {
ch = synchConsoleIn->GetChar();
if(ch != EOF) synchConsoleOut->PutChar(ch); // echo it!
} while (ch != EOF);
cout << "\n";
}
//----------------------------------------------------------------------
// Kernel::NetworkTest
// Test whether the post office is working. On machines #0 and #1, do:
//
// 1. send a message to the other machine at mail box #0
// 2. wait for the other machine's message to arrive (in our mailbox #0)
// 3. send an acknowledgment for the other machine's message
// 4. wait for an acknowledgement from the other machine to our
// original message
//
// This test works best if each Nachos machine has its own window
//----------------------------------------------------------------------
void
Kernel::NetworkTest() {
if (hostName == 0 || hostName == 1) {
// if we're machine 1, send to 0 and vice versa
int farHost = (hostName == 0 ? 1 : 0);
PacketHeader outPktHdr, inPktHdr;
MailHeader outMailHdr, inMailHdr;
char *data = "Hello there!";
char *ack = "Got it!";
char buffer[MaxMailSize];
// construct packet, mail header for original message
// To: destination machine, mailbox 0
// From: our machine, reply to: mailbox 1
outPktHdr.to = farHost;
outMailHdr.to = 0;
outMailHdr.from = 1;
outMailHdr.length = strlen(data) + 1;
// Send the first message
postOfficeOut->Send(outPktHdr, outMailHdr, data);
// Wait for the first message from the other machine
postOfficeIn->Receive(0, &inPktHdr, &inMailHdr, buffer);
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
<< inMailHdr.from << "\n";
cout.flush();
// Send acknowledgement to the other machine (using "reply to" mailbox
// in the message that just arrived
outPktHdr.to = inPktHdr.from;
outMailHdr.to = inMailHdr.from;
outMailHdr.length = strlen(ack) + 1;
postOfficeOut->Send(outPktHdr, outMailHdr, ack);
// Wait for the ack from the other machine to the first message we sent
postOfficeIn->Receive(1, &inPktHdr, &inMailHdr, buffer);
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
<< inMailHdr.from << "\n";
cout.flush();
}
// Then we're done!
}
void ForkExecute(Thread *t)
{
if ( !t->space->Load(t->getName()) ) {
return; // executable not found
}
t->space->Execute(t->getName());
}
void Kernel::ExecAll()
{
for (int i=1;i<=execfileNum;i++) {
int a = Exec(execfile[i]);
}
currentThread->Finish();
//Kernel::Exec();
}
int Kernel::Exec(char* name)
{
t[threadNum] = new Thread(name, threadNum);
t[threadNum]->space = new AddrSpace();
t[threadNum]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[threadNum]);
threadNum++;
return threadNum-1;
/*
cout << "Total threads number is " << execfileNum << endl;
for (int n=1;n<=execfileNum;n++) {
t[n] = new Thread(execfile[n]);
t[n]->space = new AddrSpace();
t[n]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[n]);
cout << "Thread " << execfile[n] << " is executing." << endl;
}
cout << "debug Kernel::Run finished.\n";
*/
// Thread *t1 = new Thread(execfile[1]);
// Thread *t1 = new Thread("../test/test1");
// Thread *t2 = new Thread("../test/test2");
// AddrSpace *halt = new AddrSpace();
// t1->space = new AddrSpace();
// t2->space = new AddrSpace();
// halt->Execute("../test/halt");
// t1->Fork((VoidFunctionPtr) &ForkExecute, (void *)t1);
// t2->Fork((VoidFunctionPtr) &ForkExecute, (void *)t2);
// currentThread->Finish();
// Kernel::Run();
// cout << "after ThreadedKernel:Run();" << endl; // unreachable
}
int Kernel::CreateFile(char *filename)
{
return fileSystem->Create(filename);
}

86
code/threads/kernel.h Executable file
View File

@@ -0,0 +1,86 @@
// kernel.h
// Global variables for the Nachos kernel.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef KERNEL_H
#define KERNEL_H
#include "copyright.h"
#include "debug.h"
#include "utility.h"
#include "thread.h"
#include "scheduler.h"
#include "interrupt.h"
#include "stats.h"
#include "alarm.h"
#include "filesys.h"
#include "machine.h"
class PostOfficeInput;
class PostOfficeOutput;
class SynchConsoleInput;
class SynchConsoleOutput;
class SynchDisk;
class Kernel {
public:
Kernel(int argc, char **argv);
// Interpret command line arguments
~Kernel(); // deallocate the kernel
void Initialize(); // initialize the kernel -- separated
// from constructor because
// refers to "kernel" as a global
void ExecAll();
int Exec(char* name);
void ThreadSelfTest(); // self test of threads and synchronization
void ConsoleTest(); // interactive console self test
void NetworkTest(); // interactive 2-machine network test
Thread* getThread(int threadID){return t[threadID];}
int CreateFile(char* filename); // fileSystem call
// These are public for notational convenience; really,
// they're global variables used everywhere.
Thread *currentThread; // the thread holding the CPU
Scheduler *scheduler; // the ready list
Interrupt *interrupt; // interrupt status
Statistics *stats; // performance metrics
Alarm *alarm; // the software alarm clock
Machine *machine; // the simulated CPU
SynchConsoleInput *synchConsoleIn;
SynchConsoleOutput *synchConsoleOut;
SynchDisk *synchDisk;
FileSystem *fileSystem;
PostOfficeInput *postOfficeIn;
PostOfficeOutput *postOfficeOut;
int hostName; // machine identifier
private:
Thread* t[10];
char* execfile[10];
int execfileNum;
int threadNum;
bool randomSlice; // enable pseudo-random time slicing
bool debugUserProg; // single step user program
double reliability; // likelihood messages are dropped
char *consoleIn; // file to read console input from
char *consoleOut; // file to send console output to
#ifndef FILESYS_STUB
bool formatFlag; // format the disk if this is true
#endif
};
#endif // KERNEL_H

297
code/threads/main.cc Executable file
View File

@@ -0,0 +1,297 @@
// main.cc
// Driver code to initialize, selftest, and run the
// operating system kernel.
//
// Usage: nachos -d <debugflags> -rs <random seed #>
// -s -x <nachos file> -ci <consoleIn> -co <consoleOut>
// -f -cp <unix file> <nachos file>
// -p <nachos file> -r <nachos file> -l -D
// -n <network reliability> -m <machine id>
// -z -K -C -N
//
// -d causes certain debugging messages to be printed (see debug.h)
// -rs causes Yield to occur at random (but repeatable) spots
// -z prints the copyright message
// -s causes user programs to be executed in single-step mode
// -x runs a user program
// -ci specify file for console input (stdin is the default)
// -co specify file for console output (stdout is the default)
// -n sets the network reliability
// -m sets this machine's host id (needed for the network)
// -K run a simple self test of kernel threads and synchronization
// -C run an interactive console test
// -N run a two-machine network test (see Kernel::NetworkTest)
//
// Filesystem-related flags:
// -f forces the Nachos disk to be formatted
// -cp copies a file from UNIX to Nachos
// -p prints a Nachos file to stdout
// -r removes a Nachos file from the file system
// -l lists the contents of the Nachos directory
// -D prints the contents of the entire file system
//
// Note: the file system flags are not used if the stub filesystem
// is being used
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#define MAIN
#include "copyright.h"
#undef MAIN
#include "main.h"
#include "filesys.h"
#include "openfile.h"
#include "sysdep.h"
// global variables
Kernel *kernel;
Debug *debug;
//----------------------------------------------------------------------
// Cleanup
// Delete kernel data structures; called when user hits "ctl-C".
//----------------------------------------------------------------------
static void
Cleanup(int x)
{
cerr << "\nCleaning up after signal " << x << "\n";
delete kernel;
}
//-------------------------------------------------------------------
// Constant used by "Copy" and "Print"
// It is the number of bytes read from the Unix file (for Copy)
// or the Nachos file (for Print) by each read operation
//-------------------------------------------------------------------
static const int TransferSize = 128;
#ifndef FILESYS_STUB
//----------------------------------------------------------------------
// Copy
// Copy the contents of the UNIX file "from" to the Nachos file "to"
//----------------------------------------------------------------------
static void
Copy(char *from, char *to)
{
int fd;
OpenFile* openFile;
int amountRead, fileLength;
char *buffer;
// Open UNIX file
if ((fd = OpenForReadWrite(from,FALSE)) < 0) {
printf("Copy: couldn't open input file %s\n", from);
return;
}
// Figure out length of UNIX file
Lseek(fd, 0, 2);
fileLength = Tell(fd);
Lseek(fd, 0, 0);
// Create a Nachos file of the same length
DEBUG('f', "Copying file " << from << " of size " << fileLength << " to file " << to);
if (!kernel->fileSystem->Create(to, fileLength)) { // Create Nachos file
printf("Copy: couldn't create output file %s\n", to);
Close(fd);
return;
}
openFile = kernel->fileSystem->Open(to);
ASSERT(openFile != NULL);
// Copy the data in TransferSize chunks
buffer = new char[TransferSize];
while ((amountRead=ReadPartial(fd, buffer, sizeof(char)*TransferSize)) > 0)
openFile->Write(buffer, amountRead);
delete [] buffer;
// Close the UNIX and the Nachos files
delete openFile;
Close(fd);
}
#endif // FILESYS_STUB
//----------------------------------------------------------------------
// Print
// Print the contents of the Nachos file "name".
//----------------------------------------------------------------------
void
Print(char *name)
{
OpenFile *openFile;
int i, amountRead;
char *buffer;
if ((openFile = kernel->fileSystem->Open(name)) == NULL) {
printf("Print: unable to open file %s\n", name);
return;
}
buffer = new char[TransferSize];
while ((amountRead = openFile->Read(buffer, TransferSize)) > 0)
for (i = 0; i < amountRead; i++)
printf("%c", buffer[i]);
delete [] buffer;
delete openFile; // close the Nachos file
return;
}
//----------------------------------------------------------------------
// main
// Bootstrap the operating system kernel.
//
// Initialize kernel data structures
// Call some test routines
// Call "Run" to start an initial user program running
//
// "argc" is the number of command line arguments (including the name
// of the command) -- ex: "nachos -d +" -> argc = 3
// "argv" is an array of strings, one for each command line argument
// ex: "nachos -d +" -> argv = {"nachos", "-d", "+"}
//----------------------------------------------------------------------
int
main(int argc, char **argv)
{
int i;
char *debugArg = "";
char *userProgName = NULL; // default is not to execute a user prog
bool threadTestFlag = false;
bool consoleTestFlag = false;
bool networkTestFlag = false;
#ifndef FILESYS_STUB
char *copyUnixFileName = NULL; // UNIX file to be copied into Nachos
char *copyNachosFileName = NULL; // name of copied file in Nachos
char *printFileName = NULL;
char *removeFileName = NULL;
bool dirListFlag = false;
bool dumpFlag = false;
#endif //FILESYS_STUB
// some command line arguments are handled here.
// those that set kernel parameters are handled in
// the Kernel constructor
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0) {
ASSERT(i + 1 < argc); // next argument is debug string
debugArg = argv[i + 1];
i++;
}
else if (strcmp(argv[i], "-z") == 0) {
cout << copyright << "\n";
}
else if (strcmp(argv[i], "-x") == 0) {
ASSERT(i + 1 < argc);
userProgName = argv[i + 1];
i++;
}
else if (strcmp(argv[i], "-K") == 0) {
threadTestFlag = TRUE;
}
else if (strcmp(argv[i], "-C") == 0) {
consoleTestFlag = TRUE;
}
else if (strcmp(argv[i], "-N") == 0) {
networkTestFlag = TRUE;
}
#ifndef FILESYS_STUB
else if (strcmp(argv[i], "-cp") == 0) {
ASSERT(i + 2 < argc);
copyUnixFileName = argv[i + 1];
copyNachosFileName = argv[i + 2];
i += 2;
}
else if (strcmp(argv[i], "-p") == 0) {
ASSERT(i + 1 < argc);
printFileName = argv[i + 1];
i++;
}
else if (strcmp(argv[i], "-r") == 0) {
ASSERT(i + 1 < argc);
removeFileName = argv[i + 1];
i++;
}
else if (strcmp(argv[i], "-l") == 0) {
dirListFlag = true;
}
else if (strcmp(argv[i], "-D") == 0) {
dumpFlag = true;
}
#endif //FILESYS_STUB
else if (strcmp(argv[i], "-u") == 0) {
cout << "Partial usage: nachos [-z -d debugFlags]\n";
cout << "Partial usage: nachos [-x programName]\n";
cout << "Partial usage: nachos [-K] [-C] [-N]\n";
#ifndef FILESYS_STUB
cout << "Partial usage: nachos [-cp UnixFile NachosFile]\n";
cout << "Partial usage: nachos [-p fileName] [-r fileName]\n";
cout << "Partial usage: nachos [-l] [-D]\n";
#endif //FILESYS_STUB
}
}
debug = new Debug(debugArg);
DEBUG(dbgThread, "Entering main");
kernel = new Kernel(argc, argv);
kernel->Initialize();
CallOnUserAbort(Cleanup); // if user hits ctl-C
// at this point, the kernel is ready to do something
// run some tests, if requested
if (threadTestFlag) {
kernel->ThreadSelfTest(); // test threads and synchronization
}
if (consoleTestFlag) {
kernel->ConsoleTest(); // interactive test of the synchronized console
}
if (networkTestFlag) {
kernel->NetworkTest(); // two-machine test of the network
}
#ifndef FILESYS_STUB
if (removeFileName != NULL) {
kernel->fileSystem->Remove(removeFileName);
}
if (copyUnixFileName != NULL && copyNachosFileName != NULL) {
Copy(copyUnixFileName,copyNachosFileName);
}
if (dumpFlag) {
kernel->fileSystem->Print();
}
if (dirListFlag) {
kernel->fileSystem->List();
}
if (printFileName != NULL) {
Print(printFileName);
}
#endif // FILESYS_STUB
// finally, run an initial user program if requested to do so
kernel->ExecAll();
// If we don't run a user program, we may get here.
// Calling "return" would terminate the program.
// Instead, call Halt, which will first clean up, then
// terminate.
// kernel->interrupt->Halt();
ASSERTNOTREACHED();
}

19
code/threads/main.h Executable file
View File

@@ -0,0 +1,19 @@
// main.h
// This file defines the Nachos global variables
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef MAIN_H
#define MAIN_H
#include "copyright.h"
#include "debug.h"
#include "kernel.h"
extern Kernel *kernel;
extern Debug *debug;
#endif // MAIN_H

179
code/threads/scheduler.cc Executable file
View File

@@ -0,0 +1,179 @@
// scheduler.cc
// Routines to choose the next thread to run, and to dispatch to
// that thread.
//
// These routines assume that interrupts are already disabled.
// If interrupts are disabled, we can assume mutual exclusion
// (since we are on a uniprocessor).
//
// NOTE: We can't use Locks to provide mutual exclusion here, since
// if we needed to wait for a lock, and the lock was busy, we would
// end up calling FindNextToRun(), and that would put us in an
// infinite loop.
//
// Very simple implementation -- no priorities, straight FIFO.
// Might need to be improved in later assignments.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "debug.h"
#include "scheduler.h"
#include "main.h"
//----------------------------------------------------------------------
// Scheduler::Scheduler
// Initialize the list of ready but not running threads.
// Initially, no ready threads.
//----------------------------------------------------------------------
Scheduler::Scheduler()
{
readyList = new List<Thread *>;
toBeDestroyed = NULL;
}
//----------------------------------------------------------------------
// Scheduler::~Scheduler
// De-allocate the list of ready threads.
//----------------------------------------------------------------------
Scheduler::~Scheduler()
{
delete readyList;
}
//----------------------------------------------------------------------
// Scheduler::ReadyToRun
// Mark a thread as ready, but not running.
// Put it on the ready list, for later scheduling onto the CPU.
//
// "thread" is the thread to be put on the ready list.
//----------------------------------------------------------------------
void
Scheduler::ReadyToRun (Thread *thread)
{
ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName());
//cout << "Putting thread on ready list: " << thread->getName() << endl ;
thread->setStatus(READY);
readyList->Append(thread);
}
//----------------------------------------------------------------------
// Scheduler::FindNextToRun
// Return the next thread to be scheduled onto the CPU.
// If there are no ready threads, return NULL.
// Side effect:
// Thread is removed from the ready list.
//----------------------------------------------------------------------
Thread *
Scheduler::FindNextToRun ()
{
ASSERT(kernel->interrupt->getLevel() == IntOff);
if (readyList->IsEmpty()) {
return NULL;
} else {
return readyList->RemoveFront();
}
}
//----------------------------------------------------------------------
// Scheduler::Run
// Dispatch the CPU to nextThread. Save the state of the old thread,
// and load the state of the new thread, by calling the machine
// dependent context switch routine, SWITCH.
//
// Note: we assume the state of the previously running thread has
// already been changed from running to blocked or ready (depending).
// Side effect:
// The global variable kernel->currentThread becomes nextThread.
//
// "nextThread" is the thread to be put into the CPU.
// "finishing" is set if the current thread is to be deleted
// once we're no longer running on its stack
// (when the next thread starts running)
//----------------------------------------------------------------------
void
Scheduler::Run (Thread *nextThread, bool finishing)
{
Thread *oldThread = kernel->currentThread;
ASSERT(kernel->interrupt->getLevel() == IntOff);
if (finishing) { // mark that we need to delete current thread
ASSERT(toBeDestroyed == NULL);
toBeDestroyed = oldThread;
}
if (oldThread->space != NULL) { // if this thread is a user program,
oldThread->SaveUserState(); // save the user's CPU registers
oldThread->space->SaveState();
}
oldThread->CheckOverflow(); // check if the old thread
// had an undetected stack overflow
kernel->currentThread = nextThread; // switch to the next thread
nextThread->setStatus(RUNNING); // nextThread is now running
DEBUG(dbgThread, "Switching from: " << oldThread->getName() << " to: " << nextThread->getName());
// This is a machine-dependent assembly language routine defined
// in switch.s. You may have to think
// a bit to figure out what happens after this, both from the point
// of view of the thread and from the perspective of the "outside world".
SWITCH(oldThread, nextThread);
// we're back, running oldThread
// interrupts are off when we return from switch!
ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Now in thread: " << oldThread->getName());
CheckToBeDestroyed(); // check if thread we were running
// before this one has finished
// and needs to be cleaned up
if (oldThread->space != NULL) { // if there is an address space
oldThread->RestoreUserState(); // to restore, do it.
oldThread->space->RestoreState();
}
}
//----------------------------------------------------------------------
// Scheduler::CheckToBeDestroyed
// If the old thread gave up the processor because it was finishing,
// we need to delete its carcass. Note we cannot delete the thread
// before now (for example, in Thread::Finish()), because up to this
// point, we were still running on the old thread's stack!
//----------------------------------------------------------------------
void
Scheduler::CheckToBeDestroyed()
{
if (toBeDestroyed != NULL) {
delete toBeDestroyed;
toBeDestroyed = NULL;
}
}
//----------------------------------------------------------------------
// Scheduler::Print
// Print the scheduler state -- in other words, the contents of
// the ready list. For debugging.
//----------------------------------------------------------------------
void
Scheduler::Print()
{
cout << "Ready list contents:\n";
readyList->Apply(ThreadPrint);
}

44
code/threads/scheduler.h Executable file
View File

@@ -0,0 +1,44 @@
// scheduler.h
// Data structures for the thread dispatcher and scheduler.
// Primarily, the list of threads that are ready to run.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef SCHEDULER_H
#define SCHEDULER_H
#include "copyright.h"
#include "list.h"
#include "thread.h"
// The following class defines the scheduler/dispatcher abstraction --
// the data structures and operations needed to keep track of which
// thread is running, and which threads are ready but not running.
class Scheduler {
public:
Scheduler(); // Initialize list of ready threads
~Scheduler(); // De-allocate ready list
void ReadyToRun(Thread* thread);
// Thread can be dispatched.
Thread* FindNextToRun(); // Dequeue first thread on the ready
// list, if any, and return thread.
void Run(Thread* nextThread, bool finishing);
// Cause nextThread to start running
void CheckToBeDestroyed();// Check if thread that had been
// running needs to be deleted
void Print(); // Print contents of ready list
// SelfTest for scheduler is implemented in class Thread
private:
List<Thread *> *readyList; // queue of threads that are ready to run,
// but not running
Thread *toBeDestroyed; // finishing thread to be destroyed
// by the next thread that runs
};
#endif // SCHEDULER_H

750
code/threads/switch.S Executable file
View File

@@ -0,0 +1,750 @@
/* switch.s
* Machine dependent context switch routines. DO NOT MODIFY THESE!
*
* Context switching is inherently machine dependent, since
* the registers to be saved, how to set up an initial
* call frame, etc, are all specific to a processor architecture.
*
* This file currently supports the following architectures:
* DEC MIPS (DECMIPS)
* DEC Alpha (ALPHA)
* SUN SPARC (SPARC)
* HP PA-RISC (PARISC)
* Intel 386 (x86)
* IBM RS6000 (PowerPC) -- I hope it will also work for Mac PowerPC
*
* We define two routines for each architecture:
*
* ThreadRoot(InitialPC, InitialArg, WhenDonePC, StartupPC)
* InitialPC - The program counter of the procedure to run
* in this thread.
* InitialArg - The single argument to the thread.
* WhenDonePC - The routine to call when the thread returns.
* StartupPC - Routine to call when the thread is started.
*
* ThreadRoot is called from the SWITCH() routine to start
* a thread for the first time.
*
* SWITCH(oldThread, newThread)
* oldThread - The current thread that was running, where the
* CPU register state is to be saved.
* newThread - The new thread to be run, where the CPU register
* state is to be loaded from.
*/
/*
Copyright (c) 1992-1996 The Regents of the University of California.
All rights reserved. See copyright.h for copyright notice and limitation
of liability and disclaimer of warranty provisions.
*/
#include "copyright.h"
#include "switch.h"
#ifdef DECMIPS
/* Symbolic register names */
#define z $0 /* zero register */
#define a0 $4 /* argument registers */
#define a1 $5
#define s0 $16 /* callee saved */
#define s1 $17
#define s2 $18
#define s3 $19
#define s4 $20
#define s5 $21
#define s6 $22
#define s7 $23
#define sp $29 /* stack pointer */
#define fp $30 /* frame pointer */
#define ra $31 /* return address */
.text
.align 2
.globl ThreadRoot
.ent ThreadRoot,0
ThreadRoot:
or fp,z,z # Clearing the frame pointer here
# makes gdb backtraces of thread stacks
# end here (I hope!)
jal StartupPC # call startup procedure
move a0, InitialArg
jal InitialPC # call main procedure
jal WhenDonePC # when done, call clean up procedure
# NEVER REACHED
.end ThreadRoot
# a0 -- pointer to old Thread
# a1 -- pointer to new Thread
.globl SWITCH
.ent SWITCH,0
SWITCH:
sw sp, SP(a0) # save new stack pointer
sw s0, S0(a0) # save all the callee-save registers
sw s1, S1(a0)
sw s2, S2(a0)
sw s3, S3(a0)
sw s4, S4(a0)
sw s5, S5(a0)
sw s6, S6(a0)
sw s7, S7(a0)
sw fp, FP(a0) # save frame pointer
sw ra, PC(a0) # save return address
lw sp, SP(a1) # load the new stack pointer
lw s0, S0(a1) # load the callee-save registers
lw s1, S1(a1)
lw s2, S2(a1)
lw s3, S3(a1)
lw s4, S4(a1)
lw s5, S5(a1)
lw s6, S6(a1)
lw s7, S7(a1)
lw fp, FP(a1)
lw ra, PC(a1) # load the return address
j ra
.end SWITCH
#endif // DECMIPS
#ifdef SPARC
/* NOTE! These files appear not to exist on Solaris --
* you need to find where (the SPARC-specific) MINFRAME, ST_FLUSH_WINDOWS, ...
* are defined. (I don't have a Solaris machine, so I have no way to tell.)
*/
#ifdef SOLARIS
#include <sys/trap.h>
#include <sys/asm_linkage.h>
#else
#include <sun4/trap.h>
#include <sun4/asm_linkage.h>
#endif
.seg "text"
/* SPECIAL to the SPARC:
* The first two instruction of ThreadRoot are skipped because
* the address of ThreadRoot is made the return address of SWITCH()
* by the routine Thread::StackAllocate. SWITCH() jumps here on the
* "ret" instruction which is really at "jmp %o7+8". The 8 skips the
* two nops at the beginning of the routine.
*/
#ifdef SOLARIS
.globl ThreadRoot
ThreadRoot:
#else
.globl _ThreadRoot
_ThreadRoot:
#endif
nop ; nop /* These 2 nops are skipped because we are called
* with a jmp+8 instruction. */
clr %fp /* Clearing the frame pointer makes gdb backtraces
* of thread stacks end here. */
/* Currently the arguments are in out registers we
* save them into local registers so they won't be
* trashed during the calls we make. */
mov InitialPC, %l0
mov InitialArg, %l1
mov WhenDonePC, %l2
/* Execute the code:
* call StartupPC();
* call InitialPC(InitialArg);
* call WhenDonePC();
*/
call StartupPC,0
nop
call %l0, 1
mov %l1, %o0 /* Using delay slot to setup argument to InitialPC */
call %l2, 0
nop
/* WhenDonePC call should never return. If it does
* we execute a trap into the debugger. */
ta ST_BREAKPOINT
#ifdef SOLARIS
.globl SWITCH
SWITCH:
#else
.globl _SWITCH
_SWITCH:
#endif
save %sp, -SA(MINFRAME), %sp
st %fp, [%i0]
st %i0, [%i0+I0]
st %i1, [%i0+I1]
st %i2, [%i0+I2]
st %i3, [%i0+I3]
st %i4, [%i0+I4]
st %i5, [%i0+I5]
st %i7, [%i0+I7]
ta ST_FLUSH_WINDOWS
nop
mov %i1, %l0
ld [%l0+I0], %i0
ld [%l0+I1], %i1
ld [%l0+I2], %i2
ld [%l0+I3], %i3
ld [%l0+I4], %i4
ld [%l0+I5], %i5
ld [%l0+I7], %i7
ld [%l0], %i6
ret
restore
#endif // SPARC
#ifdef PARISC
;rp = r2, sp = r30
;arg0 = r26, arg1 = r25, arg2 = r24, arg3 = r23
.SPACE $TEXT$
.SUBSPA $CODE$
ThreadRoot
.PROC
.CALLINFO CALLER,FRAME=0
.ENTER
.CALL
ble 0(%r6) ;call StartupPC
stw %r31, -24(%sp) ;put return address in proper stack
;location for StartupPC export stub.
or %r4, 0, %arg0 ;load InitialArg
.CALL ;in=26
ble 0(%r3) ;call InitialPC
stw %r31, -24(%sp) ;put return address in proper stack
;location for InitialPC export stub.
.CALL
ble 0(%r5) ;call WhenDonePC
stw %r31, -24(%sp) ;put return address in proper stack
;location for StartupPC export stub.
.LEAVE
.PROCEND
SWITCH
.PROC
.CALLINFO CALLER,FRAME=0
.ENTRY
; save process state of oldThread
stw %sp, SP(%arg0) ;save stack pointer
stw %r3, S0(%arg0) ;save callee-save registers
stw %r4, S1(%arg0)
stw %r5, S2(%arg0)
stw %r6, S3(%arg0)
stw %r7, S4(%arg0)
stw %r8, S5(%arg0)
stw %r9, S6(%arg0)
stw %r10, S7(%arg0)
stw %r11, S8(%arg0)
stw %r12, S9(%arg0)
stw %r13, S10(%arg0)
stw %r14, S11(%arg0)
stw %r15, S12(%arg0)
stw %r16, S13(%arg0)
stw %r17, S14(%arg0)
stw %r18, S15(%arg0)
stw %rp, PC(%arg0) ;save program counter
; restore process state of nextThread
ldw SP(%arg1), %sp ;restore stack pointer
ldw S0(%arg1), %r3 ;restore callee-save registers
ldw S1(%arg1), %r4
ldw S2(%arg1), %r5
ldw S3(%arg1), %r6
ldw S4(%arg1), %r7
ldw S5(%arg1), %r8
ldw S6(%arg1), %r9
ldw S7(%arg1), %r10
ldw S8(%arg1), %r11
ldw S9(%arg1), %r12
ldw S10(%arg1), %r13
ldw S11(%arg1), %r14
ldw S12(%arg1), %r15
ldw S13(%arg1), %r16
ldw S14(%arg1), %r17
ldw PC(%arg1), %rp ;save program counter
bv 0(%rp)
.EXIT
ldw S15(%arg1), %r18
.PROCEND
.EXPORT SWITCH,ENTRY,PRIV_LEV=3,RTNVAL=GR
.EXPORT ThreadRoot,ENTRY,PRIV_LEV=3,RTNVAL=GR
#endif // PARISC
#ifdef x86
.text
.align 2
.globl ThreadRoot
.globl _ThreadRoot
/* void ThreadRoot( void )
**
** expects the following registers to be initialized:
** eax points to startup function (interrupt enable)
** edx contains inital argument to thread function
** esi points to thread function
** edi point to Thread::Finish()
*/
_ThreadRoot:
ThreadRoot:
pushl %ebp
movl %esp,%ebp
pushl InitialArg
call *StartupPC
call *InitialPC
call *WhenDonePC
# NOT REACHED
movl %ebp,%esp
popl %ebp
ret
/* void SWITCH( thread *t1, thread *t2 )
**
** on entry, stack looks like this:
** 8(esp) -> thread *t2
** 4(esp) -> thread *t1
** (esp) -> return address
**
** we push the current eax on the stack so that we can use it as
** a pointer to t1, this decrements esp by 4, so when we use it
** to reference stuff on the stack, we add 4 to the offset.
*/
.comm _eax_save,4
.globl SWITCH
.globl _SWITCH
_SWITCH:
SWITCH:
movl %eax,_eax_save # save the value of eax
movl 4(%esp),%eax # move pointer to t1 into eax
movl %ebx,_EBX(%eax) # save registers
movl %ecx,_ECX(%eax)
movl %edx,_EDX(%eax)
movl %esi,_ESI(%eax)
movl %edi,_EDI(%eax)
movl %ebp,_EBP(%eax)
movl %esp,_ESP(%eax) # save stack pointer
movl _eax_save,%ebx # get the saved value of eax
movl %ebx,_EAX(%eax) # store it
movl 0(%esp),%ebx # get return address from stack into ebx
movl %ebx,_PC(%eax) # save it into the pc storage
movl 8(%esp),%eax # move pointer to t2 into eax
movl _EAX(%eax),%ebx # get new value for eax into ebx
movl %ebx,_eax_save # save it
movl _EBX(%eax),%ebx # retore old registers
movl _ECX(%eax),%ecx
movl _EDX(%eax),%edx
movl _ESI(%eax),%esi
movl _EDI(%eax),%edi
movl _EBP(%eax),%ebp
movl _ESP(%eax),%esp # restore stack pointer
movl _PC(%eax),%eax # restore return address into eax
movl %eax,4(%esp) # copy over the ret address on the stack
movl _eax_save,%eax
ret
#endif // x86
#if defined(ApplePowerPC)
/* The AIX PowerPC code is incompatible with the assembler on MacOS X
* and Linux. So the SWITCH code was adapted for IBM 750 compatible
* processors, and ThreadRoot is modeled after the more reasonable
* looking ThreadRoot's in this file.
*
* Joshua LeVasseur <jtl@ira.uka.de>
*/
.align 2
.globl _SWITCH
_SWITCH:
stw r1, 0(r3) /* Store stack pointer. */
stmw r13, 20(r3) /* Store general purpose registers 13 - 31. */
stfd f14, 96(r3) /* Store floating point registers 14 -31. */
stfd f15, 104(r3)
stfd f16, 112(r3)
stfd f17, 120(r3)
stfd f18, 128(r3)
stfd f19, 136(r3)
stfd f20, 144(r3)
stfd f21, 152(r3)
stfd f22, 160(r3)
stfd f23, 168(r3)
stfd f24, 176(r3)
stfd f25, 184(r3)
stfd f26, 192(r3)
stfd f27, 200(r3)
stfd f28, 208(r3)
stfd f29, 216(r3)
stfd f30, 224(r3)
stfd f31, 232(r3)
mflr r0
stw r0, 244(r3) /* Spill the link register. */
mfcr r12
stw r12, 240(r3) /* Spill the condition register. */
lwz r1, 0(r4) /* Load the incoming stack pointer. */
lwz r0, 244(r4) /* Load the incoming link register. */
mtlr r0 /* Restore the link register. */
lwz r12, 240(r4) /* Load the condition register value. */
mtcrf 0xff, r12 /* Restore the condition register. */
lmw r13, 20(r4) /* Restore registers r13 - r31. */
lfd f14, 96(r4) /* Restore floating point register f14 - f31. */
lfd f15, 104(r4)
lfd f16, 112(r4)
lfd f17, 120(r4)
lfd f18, 128(r4)
lfd f19, 136(r4)
lfd f20, 144(r4)
lfd f21, 152(r4)
lfd f22, 160(r4)
lfd f23, 168(r4)
lfd f24, 176(r4)
lfd f25, 184(r4)
lfd f26, 192(r4)
lfd f27, 200(r4)
lfd f28, 208(r4)
lfd f29, 216(r4)
lfd f30, 224(r4)
lfd f31, 232(r4)
/* When a thread first starts, the following blr instruction jumps
* to ThreadRoot. ThreadRoot expects the incoming thread block
* in r4.
*/
blr /* Branch to the address held in link register. */
.align 2
.globl _ThreadRoot
_ThreadRoot:
lwz r20, 16(r4) /* StartupPCState - ThreadBegin */
lwz r21, 8(r4) /* InitialArgState - arg */
lwz r22, 4(r4) /* InitialPCState - func */
lwz r23, 12(r4) /* WhenDonePCState - ThreadFinish */
/* Call ThreadBegin function. */
mtctr r20 /* The function pointer. */
bctrl
/* Call the target function. */
mr r3, r21 /* Function arg. */
mtctr r22 /* Function pointer. */
bctrl
/* Call the ThreadFinish function. */
mtctr r23
bctrl
/* We shouldn't execute here. */
1: b 1b
#endif
#if defined(PowerPC) && !defined(ApplePowerPC)
.globl branch[ds]
.csect branch[ds]
.long .branch[PR]
.long TOC[tc0]
.long 0
.toc
T.branch: .tc .branch[tc], branch[ds]
.globl .branch[PR]
.csect .branch[PR]
l 0, 0x0(11) # load function address into r0
mtctr 0 # move r0 into counter register
l 2, 0x4(11) # move new TOC address into r2
l 11, 0x8(11) # reset function address
bctr # branch to the counter register
.globl ThreadRoot[ds]
.csect ThreadRoot[ds]
.long .ThreadRoot[PR]
.long TOC[tc0]
.long 0
.toc
T.ThreadRoot: .tc .ThreadRoot[tc], ThreadRoot[ds]
.globl .ThreadRoot[PR]
.csect .ThreadRoot[PR]
.set argarea, 32
.set linkarea, 24
.set locstckarea, 0
.set nfprs, 18
.set ngprs, 19
.set szdsa, 8*nfprs+4*ngprs+linkarea+argarea+locstckarea
mflr 0
mfcr 12
bl ._savef14
cror 0xf, 0xf, 0xf
stm 13, -8*nfprs-4*ngprs(1)
st 0, 8(1)
st 12, 4(1)
st 4, 24(1)
st 5, 28(1)
st 6, 32(1)
stu 1, -szdsa(1)
muli 11,3,1 # copy contents of register r24 to r11
bl .branch[PR] # call function branch
cror 0xf, 0xf, 0xf # no operation
ai 1,1,szdsa
lm 13, -8*nfprs-4*ngprs(1)
bl ._restf14
cror 0xf, 0xf, 0xf
l 0, 8(1)
l 12, 4(1)
mtlr 0
mtcrf 0x38, 12
l 4, 24(1)
l 5, 28(1)
l 6, 32(1)
mflr 0
mfcr 12
bl ._savef14
cror 0xf, 0xf, 0xf
stm 13, -8*nfprs-4*ngprs(1)
st 0, 8(1)
st 12, 4(1)
st 6, 24(1)
stu 1, -szdsa(1)
muli 3, 4,1 # load user function parameter r22 to r3
muli 11,5,1 # copy contents of register r21 to r11
bl .branch[PR] # call function branch
cror 0xf, 0xf, 0xf # no operation
ai 1,1,szdsa
lm 13, -8*nfprs-4*ngprs(1)
bl ._restf14
cror 0xf, 0xf, 0xf
l 0, 8(1)
l 12, 4(1)
mtlr 0
mtcrf 0x38, 12
l 6, 24(1)
muli 11,6,1 # copy contents of register r23 to r11
bl .branch[PR] # call function branch
cror 0xf, 0xf, 0xf # no operation
brl # the programme should not return here.
.extern ._savef14
.extern ._restf14
.globl SWITCH[ds]
.csect SWITCH[ds]
.long .SWITCH[PR]
.long TOC[tc0]
.long 0
.toc
T.SWITCH: .tc .SWITCH[tc], SWITCH[ds]
.globl .SWITCH[PR]
.csect .SWITCH[PR]
st 1, 0(3) # store stack pointer
stm 13, 20(3) # store general purpose registers (13 -31)
stfd 14, 96(3) # store floating point registers (14 -31)
stfd 15, 104(3) # there is no single instruction to do for
stfd 16, 112(3) # floating point registers. so do one by one
stfd 17, 120(3)
stfd 18, 128(3)
stfd 19, 136(3)
stfd 20, 144(3)
stfd 21, 152(3)
stfd 22, 160(3)
stfd 23, 168(3)
stfd 24, 176(3)
stfd 25, 184(3)
stfd 26, 192(3)
stfd 27, 200(3)
stfd 28, 208(3)
stfd 29, 216(3)
stfd 30, 224(3)
stfd 31, 232(3)
mflr 0 # move link register value to register 0
st 0, 244(3) # store link register value
mfcr 12 # move condition register to register 12
st 12, 240(3) # store condition register value
l 1, 0(4) # load stack pointer
l 0, 244(4) # load link register value
mtlr 0
l 12, 240(4) # load condition register value
mtcrf 0x38, 12
lm 13, 20(4) # load into general purpose registers (13 -31)
lfd 14, 96(4) # load into floating point registers (14 -31)
lfd 15, 104(4) # there is no single instruction for
lfd 16, 112(4) # loading into more than one floating point
lfd 17, 120(4) # registers. so do one by one.
lfd 18, 128(4)
lfd 19, 136(4)
lfd 20, 144(4)
lfd 21, 152(4)
lfd 22, 160(4)
lfd 23, 168(4)
lfd 24, 176(4)
lfd 25, 184(4)
lfd 26, 192(4)
lfd 27, 200(4)
lfd 28, 208(4)
lfd 29, 216(4)
lfd 30, 224(4)
lfd 31, 232(4)
l 3, 16(4)
l 5, 4(4)
l 6, 12(4)
l 4, 8(4)
brl # branch to the address held in link register.
#endif // PowerPC
#ifdef ALPHA
/*
* Porting to Alpha was done by Shuichi Oikawa (shui@sfc.keio.ac.jp).
*/
/*
* Symbolic register names and register saving rules
*
* Legend:
* T Saved by caller (Temporaries)
* S Saved by callee (call-Safe registers)
*/
#define v0 $0 /* (T) return value */
#define t0 $1 /* (T) temporary registers */
#define s0 $9 /* (S) call-safe registers */
#define s1 $10
#define s2 $11
#define s3 $12
#define s4 $13
#define s5 $14
#define s6 $15
#define a0 $16 /* (T) argument registers */
#define a1 $17
#define ai $25 /* (T) argument information */
#define ra $26 /* (T) return address */
#define pv $27 /* (T) procedure value */
#define gp $29 /* (T) (local) data pointer */
#define sp $30 /* (S) stack pointer */
#define zero $31 /* wired zero */
.set noreorder # unless overridden
.align 3
.text
.globl ThreadRoot
.ent ThreadRoot,0
ThreadRoot:
.frame sp,0,ra
ldgp gp,0(pv)
mov zero,s6 # Clearing the frame pointer here
# makes gdb backtraces of thread stacks
# end here (I hope!)
mov StartupPC,pv
jsr ra,(pv) # call startup procedure
ldgp gp,0(ra)
mov InitialArg,a0
mov InitialPC,pv
jsr ra,(pv) # call main procedure
ldgp gp,0(ra)
mov WhenDonePC,pv
jsr ra,(pv) # when done, call clean up procedure
ldgp gp,0(ra)
.end ThreadRoot # NEVER REACHED
/* a0 -- pointer to old Thread *
* a1 -- pointer to new Thread */
.globl SWITCH
.ent SWITCH,0
SWITCH:
.frame sp,0,ra
ldgp gp,0(pv)
stq ra, PC(a0) # save return address
stq gp, GP(a0)
stq sp, SP(a0) # save new stack pointer
stq s0, S0(a0) # save all the callee-save registers
stq s1, S1(a0)
stq s2, S2(a0)
stq s3, S3(a0)
stq s4, S4(a0)
stq s5, S5(a0)
stq s6, S6(a0) # save frame pointer
ldq ra, PC(a1) # load the return address
ldq gp, GP(a1)
ldq sp, SP(a1) # load the new stack pointer
ldq s0, S0(a1) # load the callee-save registers
ldq s1, S1(a1)
ldq s2, S2(a1)
ldq s3, S3(a1)
ldq s4, S4(a1)
ldq s5, S5(a1)
ldq s6, S6(a1)
mov ra,pv
ret zero,(ra)
.end SWITCH
#endif // ALPHA

268
code/threads/switch.h Executable file
View File

@@ -0,0 +1,268 @@
/* switch.h
* Definitions needed for implementing context switching.
*
* Context switching is inherently machine dependent, since
* the registers to be saved, how to set up an initial
* call frame, etc, are all specific to a processor architecture.
*
* This file currently supports the DEC MIPS, DEC Alpha, SUN SPARC,
* HP PARISC, IBM PowerPC, and Intel x86 architectures.
*/
/*
Copyright (c) 1992-1996 The Regents of the University of California.
All rights reserved. See copyright.h for copyright notice and limitation
of liability and disclaimer of warranty provisions.
*/
#ifndef SWITCH_H
#define SWITCH_H
#include "copyright.h"
#ifdef DECMIPS
/* Registers that must be saved during a context switch.
* These are the offsets from the beginning of the Thread object,
* in bytes, used in switch.s
*/
#define SP 0
#define S0 4
#define S1 8
#define S2 12
#define S3 16
#define S4 20
#define S5 24
#define S6 28
#define S7 32
#define FP 36
#define PC 40
/* To fork a thread, we set up its saved register state, so that
* when we switch to the thread, it will start running in ThreadRoot.
*
* The following are the initial registers we need to set up to
* pass values into ThreadRoot (for instance, containing the procedure
* for the thread to run). The first set is the registers as used
* by ThreadRoot; the second set is the locations for these initial
* values in the Thread object -- used in Thread::AllocateStack().
*/
#define InitialPC s0
#define InitialArg s1
#define WhenDonePC s2
#define StartupPC s3
#define PCState (PC/4-1)
#define FPState (FP/4-1)
#define InitialPCState (S0/4-1)
#define InitialArgState (S1/4-1)
#define WhenDonePCState (S2/4-1)
#define StartupPCState (S3/4-1)
#endif // DECMIPS
#ifdef SPARC
/* Registers that must be saved during a context switch. See comment above. */
#define I0 4
#define I1 8
#define I2 12
#define I3 16
#define I4 20
#define I5 24
#define I6 28
#define I7 32
/* Aliases used for clearing code. */
#define FP I6
#define PC I7
/* Registers for ThreadRoot. See comment above. */
#define InitialPC %o0
#define InitialArg %o1
#define WhenDonePC %o2
#define StartupPC %o3
#define PCState (PC/4-1)
#define InitialPCState (I0/4-1)
#define InitialArgState (I1/4-1)
#define WhenDonePCState (I2/4-1)
#define StartupPCState (I3/4-1)
#endif // SPARC
#ifdef PARISC
/* Registers that must be saved during a context switch. See comment above. */
#define SP 0
#define S0 4
#define S1 8
#define S2 12
#define S3 16
#define S4 20
#define S5 24
#define S6 28
#define S7 32
#define S8 36
#define S9 40
#define S10 44
#define S11 48
#define S12 52
#define S13 56
#define S14 60
#define S15 64
#define PC 68
/* Registers for ThreadRoot. See comment above. */
#define InitialPC %r3 /* S0 */
#define InitialArg %r4
#define WhenDonePC %r5
#define StartupPC %r6
#define PCState (PC/4-1)
#define InitialPCState (S0/4-1)
#define InitialArgState (S1/4-1)
#define WhenDonePCState (S2/4-1)
#define StartupPCState (S3/4-1)
#endif // PARISC
#ifdef x86
/* the offsets of the registers from the beginning of the thread object */
#define _ESP 0
#define _EAX 4
#define _EBX 8
#define _ECX 12
#define _EDX 16
#define _EBP 20
#define _ESI 24
#define _EDI 28
#define _PC 32
/* These definitions are used in Thread::AllocateStack(). */
#define PCState (_PC/4-1)
#define FPState (_EBP/4-1)
#define InitialPCState (_ESI/4-1)
#define InitialArgState (_EDX/4-1)
#define WhenDonePCState (_EDI/4-1)
#define StartupPCState (_ECX/4-1)
#define InitialPC %esi
#define InitialArg %edx
#define WhenDonePC %edi
#define StartupPC %ecx
#endif // x86
#ifdef PowerPC
#define SP 0 // stack pointer
#define P1 4 // parameters
#define P2 8
#define P3 12
#define P4 16
#define GP13 20 // general purpose registers 13-31
#define GP14 24
#define GP15 28
#define GP16 32
#define GP17 36
#define GP18 40
#define GP19 44
#define GP20 48
#define GP21 52
#define GP22 56
#define GP23 60
#define GP24 64
#define GP25 68
#define GP26 72
#define GP27 76
#define GP28 80
#define GP29 84
#define GP30 88
#define GP31 92
#define FP13 96 // floating point registers 14-31
#define FP15 104
#define FP16 112
#define FP17 120
#define FP18 128
#define FP19 136
#define FP20 144
#define FP21 152
#define FP22 160
#define FP23 168
#define FP24 176
#define FP25 184
#define FP26 192
#define FP27 200
#define FP28 208
#define FP29 216
#define FP30 224
#define FP31 232
#define CR 240 // control register
#define LR 244 // link register
#define TOC 248 // Table Of Contents
// for ThreadRoot assembly function
#define InitialPCState 0 // (P1/4 - 1) // user function address
#define InitialArgState 1 // (P2/4 - 1) // user function argument
#define WhenDonePCState 2 // (P3/4 - 1) // clean up function addr
#define StartupPCState 3 // (P4/4 - 1) // start up function addr
#define PCState 60 // (LR/4 - 1) // ThreadRoot addr (first time).
// Later PC addr when SWITCH
// occured
#define InitialLR 21
#define InitialArg 22
#define WhenDoneLR 23
#define StartupLR 24
#endif // PowerPC
#ifdef ALPHA
/*
* Porting to Alpha was done by Shuichi Oikawa (shui@sfc.keio.ac.jp).
*/
/* Registers that must be saved during a context switch.
* These are the offsets from the beginning of the Thread object,
* in bytes, used in switch.s
*/
#define SP (0*8)
#define S0 (1*8)
#define S1 (2*8)
#define S2 (3*8)
#define S3 (4*8)
#define S4 (5*8)
#define S5 (6*8)
#define S6 (7*8) /* used as FP (Frame Pointer) */
#define GP (8*8)
#define PC (9*8)
/* To fork a thread, we set up its saved register state, so that
* when we switch to the thread, it will start running in ThreadRoot.
*
* The following are the initial registers we need to set up to
* pass values into ThreadRoot (for instance, containing the procedure
* for the thread to run). The first set is the registers as used
* by ThreadRoot; the second set is the locations for these initial
* values in the Thread object -- used in Thread::StackAllocate().
*/
#define InitialPC s0
#define InitialArg s1
#define WhenDonePC s2
#define StartupPC s3
#define PCState (PC/8-1)
#define FPState (S6/8-1)
#define InitialPCState (S0/8-1)
#define InitialArgState (S1/8-1)
#define WhenDonePCState (S2/8-1)
#define StartupPCState (S3/8-1)
#endif // HOST_ALPHA
#endif // SWITCH_H

297
code/threads/synch.cc Executable file
View File

@@ -0,0 +1,297 @@
// synch.cc
// Routines for synchronizing threads. Three kinds of
// synchronization routines are defined here: semaphores, locks
// and condition variables.
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation. We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts. While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Once we'e implemented one set of higher level atomic operations,
// we can implement others using that implementation. We illustrate
// this by implementing locks and condition variables on top of
// semaphores, instead of directly enabling and disabling interrupts.
//
// Locks are implemented using a semaphore to keep track of
// whether the lock is held or not -- a semaphore value of 0 means
// the lock is busy; a semaphore value of 1 means the lock is free.
//
// The implementation of condition variables using semaphores is
// a bit trickier, as explained below under Condition::Wait.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "synch.h"
#include "main.h"
//----------------------------------------------------------------------
// Semaphore::Semaphore
// Initialize a semaphore, so that it can be used for synchronization.
//
// "debugName" is an arbitrary name, useful for debugging.
// "initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------
Semaphore::Semaphore(char* debugName, int initialValue)
{
name = debugName;
value = initialValue;
queue = new List<Thread *>;
}
//----------------------------------------------------------------------
// Semaphore::Semaphore
// De-allocate semaphore, when no longer needed. Assume no one
// is still waiting on the semaphore!
//----------------------------------------------------------------------
Semaphore::~Semaphore()
{
delete queue;
}
//----------------------------------------------------------------------
// Semaphore::P
// Wait until semaphore value > 0, then decrement. Checking the
// value and decrementing must be done atomically, so we
// need to disable interrupts before checking the value.
//
// Note that Thread::Sleep assumes that interrupts are disabled
// when it is called.
//----------------------------------------------------------------------
void
Semaphore::P()
{
Interrupt *interrupt = kernel->interrupt;
Thread *currentThread = kernel->currentThread;
// disable interrupts
IntStatus oldLevel = interrupt->SetLevel(IntOff);
while (value == 0) { // semaphore not available
queue->Append(currentThread); // so go to sleep
currentThread->Sleep(FALSE);
}
value--; // semaphore available, consume its value
// re-enable interrupts
(void) interrupt->SetLevel(oldLevel);
}
//----------------------------------------------------------------------
// Semaphore::V
// Increment semaphore value, waking up a waiter if necessary.
// As with P(), this operation must be atomic, so we need to disable
// interrupts. Scheduler::ReadyToRun() assumes that interrupts
// are disabled when it is called.
//----------------------------------------------------------------------
void
Semaphore::V()
{
Interrupt *interrupt = kernel->interrupt;
// disable interrupts
IntStatus oldLevel = interrupt->SetLevel(IntOff);
if (!queue->IsEmpty()) { // make thread ready.
kernel->scheduler->ReadyToRun(queue->RemoveFront());
}
value++;
// re-enable interrupts
(void) interrupt->SetLevel(oldLevel);
}
//----------------------------------------------------------------------
// Semaphore::SelfTest, SelfTestHelper
// Test the semaphore implementation, by using a semaphore
// to control two threads ping-ponging back and forth.
//----------------------------------------------------------------------
static Semaphore *ping;
static void
SelfTestHelper (Semaphore *pong)
{
for (int i = 0; i < 10; i++) {
ping->P();
pong->V();
}
}
void
Semaphore::SelfTest()
{
Thread *helper = new Thread("ping", 1);
ASSERT(value == 0); // otherwise test won't work!
ping = new Semaphore("ping", 0);
helper->Fork((VoidFunctionPtr) SelfTestHelper, this);
for (int i = 0; i < 10; i++) {
ping->V();
this->P();
}
delete ping;
}
//----------------------------------------------------------------------
// Lock::Lock
// Initialize a lock, so that it can be used for synchronization.
// Initially, unlocked.
//
// "debugName" is an arbitrary name, useful for debugging.
//----------------------------------------------------------------------
Lock::Lock(char* debugName)
{
name = debugName;
semaphore = new Semaphore("lock", 1); // initially, unlocked
lockHolder = NULL;
}
//----------------------------------------------------------------------
// Lock::~Lock
// Deallocate a lock
//----------------------------------------------------------------------
Lock::~Lock()
{
delete semaphore;
}
//----------------------------------------------------------------------
// Lock::Acquire
// Atomically wait until the lock is free, then set it to busy.
// Equivalent to Semaphore::P(), with the semaphore value of 0
// equal to busy, and semaphore value of 1 equal to free.
//----------------------------------------------------------------------
void Lock::Acquire()
{
semaphore->P();
lockHolder = kernel->currentThread;
}
//----------------------------------------------------------------------
// Lock::Release
// Atomically set lock to be free, waking up a thread waiting
// for the lock, if any.
// Equivalent to Semaphore::V(), with the semaphore value of 0
// equal to busy, and semaphore value of 1 equal to free.
//
// By convention, only the thread that acquired the lock
// may release it.
//---------------------------------------------------------------------
void Lock::Release()
{
ASSERT(IsHeldByCurrentThread());
lockHolder = NULL;
semaphore->V();
}
//----------------------------------------------------------------------
// Condition::Condition
// Initialize a condition variable, so that it can be
// used for synchronization. Initially, no one is waiting
// on the condition.
//
// "debugName" is an arbitrary name, useful for debugging.
//----------------------------------------------------------------------
Condition::Condition(char* debugName)
{
name = debugName;
waitQueue = new List<Semaphore *>;
}
//----------------------------------------------------------------------
// Condition::Condition
// Deallocate the data structures implementing a condition variable.
//----------------------------------------------------------------------
Condition::~Condition()
{
delete waitQueue;
}
//----------------------------------------------------------------------
// Condition::Wait
// Atomically release monitor lock and go to sleep.
// Our implementation uses semaphores to implement this, by
// allocating a semaphore for each waiting thread. The signaller
// will V() this semaphore, so there is no chance the waiter
// will miss the signal, even though the lock is released before
// calling P().
//
// Note: we assume Mesa-style semantics, which means that the
// waiter must re-acquire the monitor lock when waking up.
//
// "conditionLock" -- lock protecting the use of this condition
//----------------------------------------------------------------------
void Condition::Wait(Lock* conditionLock)
{
Semaphore *waiter;
ASSERT(conditionLock->IsHeldByCurrentThread());
waiter = new Semaphore("condition", 0);
waitQueue->Append(waiter);
conditionLock->Release();
waiter->P();
conditionLock->Acquire();
delete waiter;
}
//----------------------------------------------------------------------
// Condition::Signal
// Wake up a thread waiting on this condition, if any.
//
// Note: we assume Mesa-style semantics, which means that the
// signaller doesn't give up control immediately to the thread
// being woken up (unlike Hoare-style).
//
// Also note: we assume the caller holds the monitor lock
// (unlike what is described in Birrell's paper). This allows
// us to access waitQueue without disabling interrupts.
//
// "conditionLock" -- lock protecting the use of this condition
//----------------------------------------------------------------------
void Condition::Signal(Lock* conditionLock)
{
Semaphore *waiter;
ASSERT(conditionLock->IsHeldByCurrentThread());
if (!waitQueue->IsEmpty()) {
waiter = waitQueue->RemoveFront();
waiter->V();
}
}
//----------------------------------------------------------------------
// Condition::Broadcast
// Wake up all threads waiting on this condition, if any.
//
// "conditionLock" -- lock protecting the use of this condition
//----------------------------------------------------------------------
void Condition::Broadcast(Lock* conditionLock)
{
while (!waitQueue->IsEmpty()) {
Signal(conditionLock);
}
}

144
code/threads/synch.h Executable file
View File

@@ -0,0 +1,144 @@
// synch.h
// Data structures for synchronizing threads.
//
// Three kinds of synchronization are defined here: semaphores,
// locks, and condition variables. The implementation for
// semaphores is given; for the latter two, only the procedure
// interface is given -- they are to be implemented as part of
// the first assignment.
//
// Note that all the synchronization objects take a "name" as
// part of the initialization. This is solely for debugging purposes.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// synch.h -- synchronization primitives.
#ifndef SYNCH_H
#define SYNCH_H
#include "copyright.h"
#include "thread.h"
#include "list.h"
#include "main.h"
// The following class defines a "semaphore" whose value is a non-negative
// integer. The semaphore has only two operations P() and V():
//
// P() -- waits until value > 0, then decrement
//
// V() -- increment, waking up a thread waiting in P() if necessary
//
// Note that the interface does *not* allow a thread to read the value of
// the semaphore directly -- even if you did read the value, the
// only thing you would know is what the value used to be. You don't
// know what the value is now, because by the time you get the value
// into a register, a context switch might have occurred,
// and some other thread might have called P or V, so the true value might
// now be different.
class Semaphore {
public:
Semaphore(char* debugName, int initialValue); // set initial value
~Semaphore(); // de-allocate semaphore
char* getName() { return name;} // debugging assist
void P(); // these are the only operations on a semaphore
void V(); // they are both *atomic*
void SelfTest(); // test routine for semaphore implementation
private:
char* name; // useful for debugging
int value; // semaphore value, always >= 0
List<Thread *> *queue;
// threads waiting in P() for the value to be > 0
};
// The following class defines a "lock". A lock can be BUSY or FREE.
// There are only two operations allowed on a lock:
//
// Acquire -- wait until the lock is FREE, then set it to BUSY
//
// Release -- set lock to be FREE, waking up a thread waiting
// in Acquire if necessary
//
// In addition, by convention, only the thread that acquired the lock
// may release it. As with semaphores, you can't read the lock value
// (because the value might change immediately after you read it).
class Lock {
public:
Lock(char* debugName); // initialize lock to be FREE
~Lock(); // deallocate lock
char* getName() { return name; } // debugging assist
void Acquire(); // these are the only operations on a lock
void Release(); // they are both *atomic*
bool IsHeldByCurrentThread() {
return lockHolder == kernel->currentThread; }
// return true if the current thread
// holds this lock.
// Note: SelfTest routine provided by SynchList
private:
char *name; // debugging assist
Thread *lockHolder; // thread currently holding lock
Semaphore *semaphore; // we use a semaphore to implement lock
};
// The following class defines a "condition variable". A condition
// variable does not have a value, but threads may be queued, waiting
// on the variable. These are only operations on a condition variable:
//
// Wait() -- release the lock, relinquish the CPU until signaled,
// then re-acquire the lock
//
// Signal() -- wake up a thread, if there are any waiting on
// the condition
//
// Broadcast() -- wake up all threads waiting on the condition
//
// All operations on a condition variable must be made while
// the current thread has acquired a lock. Indeed, all accesses
// to a given condition variable must be protected by the same lock.
// In other words, mutual exclusion must be enforced among threads calling
// the condition variable operations.
//
// In Nachos, condition variables are assumed to obey *Mesa*-style
// semantics. When a Signal or Broadcast wakes up another thread,
// it simply puts the thread on the ready list, and it is the responsibility
// of the woken thread to re-acquire the lock (this re-acquire is
// taken care of within Wait()). By contrast, some define condition
// variables according to *Hoare*-style semantics -- where the signalling
// thread gives up control over the lock and the CPU to the woken thread,
// which runs immediately and gives back control over the lock to the
// signaller when the woken thread leaves the critical section.
//
// The consequence of using Mesa-style semantics is that some other thread
// can acquire the lock, and change data structures, before the woken
// thread gets a chance to run. The advantage to Mesa-style semantics
// is that it is a lot easier to implement than Hoare-style.
class Condition {
public:
Condition(char* debugName); // initialize condition to
// "no one waiting"
~Condition(); // deallocate the condition
char* getName() { return (name); }
void Wait(Lock *conditionLock); // these are the 3 operations on
// condition variables; releasing the
// lock and going to sleep are
// *atomic* in Wait()
void Signal(Lock *conditionLock); // conditionLock must be held by
void Broadcast(Lock *conditionLock);// the currentThread for all of
// these operations
// SelfTest routine provided by SyncLists
private:
char* name;
List<Semaphore *> *waitQueue; // list of waiting threads
};
#endif // SYNCH_H

130
code/threads/synchlist.cc Executable file
View File

@@ -0,0 +1,130 @@
// synchlist.cc
// Routines for synchronized access to a list.
//
// Implemented in "monitor"-style -- surround each procedure with a
// lock acquire and release pair, using condition signal and wait for
// synchronization.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "synchlist.h"
//----------------------------------------------------------------------
// SynchList<T>::SynchList
// Allocate and initialize the data structures needed for a
// synchronized list, empty to start with.
// Elements can now be added to the list.
//----------------------------------------------------------------------
template <class T>
SynchList<T>::SynchList()
{
list = new List<T>;
lock = new Lock("list lock");
listEmpty = new Condition("list empty cond");
}
//----------------------------------------------------------------------
// SynchList<T>::~SynchList
// De-allocate the data structures created for synchronizing a list.
//----------------------------------------------------------------------
template <class T>
SynchList<T>::~SynchList()
{
delete listEmpty;
delete lock;
delete list;
}
//----------------------------------------------------------------------
// SynchList<T>::Append
// Append an "item" to the end of the list. Wake up anyone
// waiting for an element to be appended.
//
// "item" is the thing to put on the list.
//----------------------------------------------------------------------
template <class T>
void
SynchList<T>::Append(T item)
{
lock->Acquire(); // enforce mutual exclusive access to the list
list->Append(item);
listEmpty->Signal(lock); // wake up a waiter, if any
lock->Release();
}
//----------------------------------------------------------------------
// SynchList<T>::RemoveFront
// Remove an "item" from the beginning of the list. Wait if
// the list is empty.
// Returns:
// The removed item.
//----------------------------------------------------------------------
template <class T>
T
SynchList<T>::RemoveFront()
{
T item;
lock->Acquire(); // enforce mutual exclusion
while (list->IsEmpty())
listEmpty->Wait(lock); // wait until list isn't empty
item = list->RemoveFront();
lock->Release();
return item;
}
//----------------------------------------------------------------------
// SynchList<T>::Apply
// Apply function to every item on a list.
//
// "func" -- the function to apply
//----------------------------------------------------------------------
template <class T>
void
SynchList<T>::Apply(void (*func)(T))
{
lock->Acquire(); // enforce mutual exclusion
list->Apply(func);
lock->Release();
}
//----------------------------------------------------------------------
// SynchList<T>::SelfTest, SelfTestHelper
// Test whether the SynchList implementation is working,
// by having two threads ping-pong a value between them
// using two synchronized lists.
//----------------------------------------------------------------------
template <class T>
void
SynchList<T>::SelfTestHelper (void* data)
{
SynchList<T>* _this = (SynchList<T>*)data;
for (int i = 0; i < 10; i++) {
_this->Append(_this->selfTestPing->RemoveFront());
}
}
template <class T>
void
SynchList<T>::SelfTest(T val)
{
Thread *helper = new Thread("ping", 1);
ASSERT(list->IsEmpty());
selfTestPing = new SynchList<T>;
helper->Fork(SynchList<T>::SelfTestHelper, this);
for (int i = 0; i < 10; i++) {
selfTestPing->Append(val);
ASSERT(val == this->RemoveFront());
}
delete selfTestPing;
}

51
code/threads/synchlist.h Executable file
View File

@@ -0,0 +1,51 @@
// synchlist.h
// Data structures for synchronized access to a list.
//
// Identical interface to List, except accesses are synchronized.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef SYNCHLIST_H
#define SYNCHLIST_H
#include "copyright.h"
#include "list.h"
#include "synch.h"
// The following class defines a "synchronized list" -- a list for which
// these constraints hold:
// 1. Threads trying to remove an item from a list will
// wait until the list has an element on it.
// 2. One thread at a time can access list data structures
template <class T>
class SynchList {
public:
SynchList(); // initialize a synchronized list
~SynchList(); // de-allocate a synchronized list
void Append(T item); // append item to the end of the list,
// and wake up any thread waiting in remove
T RemoveFront(); // remove the first item from the front of
// the list, waiting if the list is empty
void Apply(void (*f)(T)); // apply function to all elements in list
void SelfTest(T value); // test the SynchList implementation
private:
List<T> *list; // the list of things
Lock *lock; // enforce mutual exclusive access to the list
Condition *listEmpty; // wait in Remove if the list is empty
// these are only to assist SelfTest()
SynchList<T> *selfTestPing;
static void SelfTestHelper(void* data);
};
#include "synchlist.cc"
#endif // SYNCHLIST_H

435
code/threads/thread.cc Executable file
View File

@@ -0,0 +1,435 @@
// thread.cc
// Routines to manage threads. These are the main operations:
//
// Fork -- create a thread to run a procedure concurrently
// with the caller (this is done in two steps -- first
// allocate the Thread object, then call Fork on it)
// Begin -- called when the forked procedure starts up, to turn
// interrupts on and clean up after last thread
// Finish -- called when the forked procedure finishes, to clean up
// Yield -- relinquish control over the CPU to another ready thread
// Sleep -- relinquish control over the CPU, but thread is now blocked.
// In other words, it will not run again, until explicitly
// put back on the ready queue.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "thread.h"
#include "switch.h"
#include "synch.h"
#include "sysdep.h"
// this is put at the top of the execution stack, for detecting stack overflows
const int STACK_FENCEPOST = 0xdedbeef;
//----------------------------------------------------------------------
// Thread::Thread
// Initialize a thread control block, so that we can then call
// Thread::Fork.
//
// "threadName" is an arbitrary string, useful for debugging.
//----------------------------------------------------------------------
Thread::Thread(char* threadName, int threadID)
{
ID = threadID;
name = threadName;
stackTop = NULL;
stack = NULL;
status = JUST_CREATED;
for (int i = 0; i < MachineStateSize; i++) {
machineState[i] = NULL; // not strictly necessary, since
// new thread ignores contents
// of machine registers
}
space = NULL;
}
//----------------------------------------------------------------------
// Thread::~Thread
// De-allocate a thread.
//
// NOTE: the current thread *cannot* delete itself directly,
// since it is still running on the stack that we need to delete.
//
// NOTE: if this is the main thread, we can't delete the stack
// because we didn't allocate it -- we got it automatically
// as part of starting up Nachos.
//----------------------------------------------------------------------
Thread::~Thread()
{
DEBUG(dbgThread, "Deleting thread: " << name);
ASSERT(this != kernel->currentThread);
if (stack != NULL)
DeallocBoundedArray((char *) stack, StackSize * sizeof(int));
}
//----------------------------------------------------------------------
// Thread::Fork
// Invoke (*func)(arg), allowing caller and callee to execute
// concurrently.
//
// NOTE: although our definition allows only a single argument
// to be passed to the procedure, it is possible to pass multiple
// arguments by making them fields of a structure, and passing a pointer
// to the structure as "arg".
//
// Implemented as the following steps:
// 1. Allocate a stack
// 2. Initialize the stack so that a call to SWITCH will
// cause it to run the procedure
// 3. Put the thread on the ready queue
//
// "func" is the procedure to run concurrently.
// "arg" is a single argument to be passed to the procedure.
//----------------------------------------------------------------------
void
Thread::Fork(VoidFunctionPtr func, void *arg)
{
Interrupt *interrupt = kernel->interrupt;
Scheduler *scheduler = kernel->scheduler;
IntStatus oldLevel;
DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int) func << " " << arg);
StackAllocate(func, arg);
oldLevel = interrupt->SetLevel(IntOff);
scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts
// are disabled!
(void) interrupt->SetLevel(oldLevel);
}
//----------------------------------------------------------------------
// Thread::CheckOverflow
// Check a thread's stack to see if it has overrun the space
// that has been allocated for it. If we had a smarter compiler,
// we wouldn't need to worry about this, but we don't.
//
// NOTE: Nachos will not catch all stack overflow conditions.
// In other words, your program may still crash because of an overflow.
//
// If you get bizarre results (such as seg faults where there is no code)
// then you *may* need to increase the stack size. You can avoid stack
// overflows by not putting large data structures on the stack.
// Don't do this: void foo() { int bigArray[10000]; ... }
//----------------------------------------------------------------------
void
Thread::CheckOverflow()
{
if (stack != NULL) {
#ifdef HPUX // Stacks grow upward on the Snakes
ASSERT(stack[StackSize - 1] == STACK_FENCEPOST);
#else
ASSERT(*stack == STACK_FENCEPOST);
#endif
}
}
//----------------------------------------------------------------------
// Thread::Begin
// Called by ThreadRoot when a thread is about to begin
// executing the forked procedure.
//
// It's main responsibilities are:
// 1. deallocate the previously running thread if it finished
// (see Thread::Finish())
// 2. enable interrupts (so we can get time-sliced)
//----------------------------------------------------------------------
void
Thread::Begin ()
{
ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Beginning thread: " << name);
kernel->scheduler->CheckToBeDestroyed();
kernel->interrupt->Enable();
}
//----------------------------------------------------------------------
// Thread::Finish
// Called by ThreadRoot when a thread is done executing the
// forked procedure.
//
// NOTE: we can't immediately de-allocate the thread data structure
// or the execution stack, because we're still running in the thread
// and we're still on the stack! Instead, we tell the scheduler
// to call the destructor, once it is running in the context of a different thread.
//
// NOTE: we disable interrupts, because Sleep() assumes interrupts
// are disabled.
//----------------------------------------------------------------------
//
void
Thread::Finish ()
{
(void) kernel->interrupt->SetLevel(IntOff);
ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Finishing thread: " << name);
Sleep(TRUE); // invokes SWITCH
// not reached
}
//----------------------------------------------------------------------
// Thread::Yield
// Relinquish the CPU if any other thread is ready to run.
// If so, put the thread on the end of the ready list, so that
// it will eventually be re-scheduled.
//
// NOTE: returns immediately if no other thread on the ready queue.
// Otherwise returns when the thread eventually works its way
// to the front of the ready list and gets re-scheduled.
//
// NOTE: we disable interrupts, so that looking at the thread
// on the front of the ready list, and switching to it, can be done
// atomically. On return, we re-set the interrupt level to its
// original state, in case we are called with interrupts disabled.
//
// Similar to Thread::Sleep(), but a little different.
//----------------------------------------------------------------------
void
Thread::Yield ()
{
Thread *nextThread;
IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff);
ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Yielding thread: " << name);
nextThread = kernel->scheduler->FindNextToRun();
if (nextThread != NULL) {
kernel->scheduler->ReadyToRun(this);
kernel->scheduler->Run(nextThread, FALSE);
}
(void) kernel->interrupt->SetLevel(oldLevel);
}
//----------------------------------------------------------------------
// Thread::Sleep
// Relinquish the CPU, because the current thread has either
// finished or is blocked waiting on a synchronization
// variable (Semaphore, Lock, or Condition). In the latter case,
// eventually some thread will wake this thread up, and put it
// back on the ready queue, so that it can be re-scheduled.
//
// NOTE: if there are no threads on the ready queue, that means
// we have no thread to run. "Interrupt::Idle" is called
// to signify that we should idle the CPU until the next I/O interrupt
// occurs (the only thing that could cause a thread to become
// ready to run).
//
// NOTE: we assume interrupts are already disabled, because it
// is called from the synchronization routines which must
// disable interrupts for atomicity. We need interrupts off
// so that there can't be a time slice between pulling the first thread
// off the ready list, and switching to it.
//----------------------------------------------------------------------
void
Thread::Sleep (bool finishing)
{
Thread *nextThread;
ASSERT(this == kernel->currentThread);
ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Sleeping thread: " << name);
status = BLOCKED;
//cout << "debug Thread::Sleep " << name << "wait for Idle\n";
while ((nextThread = kernel->scheduler->FindNextToRun()) == NULL) {
kernel->interrupt->Idle(); // no one to run, wait for an interrupt
}
// returns when it's time for us to run
kernel->scheduler->Run(nextThread, finishing);
}
//----------------------------------------------------------------------
// ThreadBegin, ThreadFinish, ThreadPrint
// Dummy functions because C++ does not (easily) allow pointers to member
// functions. So we create a dummy C function
// (which we can pass a pointer to), that then simply calls the
// member function.
//----------------------------------------------------------------------
static void ThreadFinish() { kernel->currentThread->Finish(); }
static void ThreadBegin() { kernel->currentThread->Begin(); }
void ThreadPrint(Thread *t) { t->Print(); }
#ifdef PARISC
//----------------------------------------------------------------------
// PLabelToAddr
// On HPUX, function pointers don't always directly point to code,
// so we need to do the conversion.
//----------------------------------------------------------------------
static void *
PLabelToAddr(void *plabel)
{
int funcPtr = (int) plabel;
if (funcPtr & 0x02) {
// L-Field is set. This is a PLT pointer
funcPtr -= 2; // Get rid of the L bit
return (*(void **)funcPtr);
} else {
// L-field not set.
return plabel;
}
}
#endif
//----------------------------------------------------------------------
// Thread::StackAllocate
// Allocate and initialize an execution stack. The stack is
// initialized with an initial stack frame for ThreadRoot, which:
// enables interrupts
// calls (*func)(arg)
// calls Thread::Finish
//
// "func" is the procedure to be forked
// "arg" is the parameter to be passed to the procedure
//----------------------------------------------------------------------
void
Thread::StackAllocate (VoidFunctionPtr func, void *arg)
{
stack = (int *) AllocBoundedArray(StackSize * sizeof(int));
#ifdef PARISC
// HP stack works from low addresses to high addresses
// everyone else works the other way: from high addresses to low addresses
stackTop = stack + 16; // HP requires 64-byte frame marker
stack[StackSize - 1] = STACK_FENCEPOST;
#endif
#ifdef SPARC
stackTop = stack + StackSize - 96; // SPARC stack must contains at
// least 1 activation record
// to start with.
*stack = STACK_FENCEPOST;
#endif
#ifdef PowerPC // RS6000
stackTop = stack + StackSize - 16; // RS6000 requires 64-byte frame marker
*stack = STACK_FENCEPOST;
#endif
#ifdef DECMIPS
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
*stack = STACK_FENCEPOST;
#endif
#ifdef ALPHA
stackTop = stack + StackSize - 8; // -8 to be on the safe side!
*stack = STACK_FENCEPOST;
#endif
#ifdef x86
// the x86 passes the return address on the stack. In order for SWITCH()
// to go to ThreadRoot when we switch to this thread, the return addres
// used in SWITCH() must be the starting address of ThreadRoot.
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
*(--stackTop) = (int) ThreadRoot;
*stack = STACK_FENCEPOST;
#endif
#ifdef PARISC
machineState[PCState] = PLabelToAddr(ThreadRoot);
machineState[StartupPCState] = PLabelToAddr(ThreadBegin);
machineState[InitialPCState] = PLabelToAddr(func);
machineState[InitialArgState] = arg;
machineState[WhenDonePCState] = PLabelToAddr(ThreadFinish);
#else
machineState[PCState] = (void*)ThreadRoot;
machineState[StartupPCState] = (void*)ThreadBegin;
machineState[InitialPCState] = (void*)func;
machineState[InitialArgState] = (void*)arg;
machineState[WhenDonePCState] = (void*)ThreadFinish;
#endif
}
#include "machine.h"
//----------------------------------------------------------------------
// Thread::SaveUserState
// Save the CPU state of a user program on a context switch.
//
// Note that a user program thread has *two* sets of CPU registers --
// one for its state while executing user code, one for its state
// while executing kernel code. This routine saves the former.
//----------------------------------------------------------------------
void
Thread::SaveUserState()
{
for (int i = 0; i < NumTotalRegs; i++)
userRegisters[i] = kernel->machine->ReadRegister(i);
}
//----------------------------------------------------------------------
// Thread::RestoreUserState
// Restore the CPU state of a user program on a context switch.
//
// Note that a user program thread has *two* sets of CPU registers --
// one for its state while executing user code, one for its state
// while executing kernel code. This routine restores the former.
//----------------------------------------------------------------------
void
Thread::RestoreUserState()
{
for (int i = 0; i < NumTotalRegs; i++)
kernel->machine->WriteRegister(i, userRegisters[i]);
}
//----------------------------------------------------------------------
// SimpleThread
// Loop 5 times, yielding the CPU to another ready thread
// each iteration.
//
// "which" is simply a number identifying the thread, for debugging
// purposes.
//----------------------------------------------------------------------
static void
SimpleThread(int which)
{
int num;
for (num = 0; num < 5; num++) {
cout << "*** thread " << which << " looped " << num << " times\n";
kernel->currentThread->Yield();
}
}
//----------------------------------------------------------------------
// Thread::SelfTest
// Set up a ping-pong between two threads, by forking a thread
// to call SimpleThread, and then calling SimpleThread ourselves.
//----------------------------------------------------------------------
void
Thread::SelfTest()
{
DEBUG(dbgThread, "Entering Thread::SelfTest");
Thread *t = new Thread("forked thread", 1);
t->Fork((VoidFunctionPtr) SimpleThread, (void *) 1);
kernel->currentThread->Yield();
SimpleThread(0);
}

152
code/threads/thread.h Executable file
View File

@@ -0,0 +1,152 @@
// thread.h
// Data structures for managing threads. A thread represents
// sequential execution of code within a program.
// So the state of a thread includes the program counter,
// the processor registers, and the execution stack.
//
// Note that because we allocate a fixed size stack for each
// thread, it is possible to overflow the stack -- for instance,
// by recursing to too deep a level. The most common reason
// for this occuring is allocating large data structures
// on the stack. For instance, this will cause problems:
//
// void foo() { int buf[1000]; ...}
//
// Instead, you should allocate all data structures dynamically:
//
// void foo() { int *buf = new int[1000]; ...}
//
//
// Bad things happen if you overflow the stack, and in the worst
// case, the problem may not be caught explicitly. Instead,
// the only symptom may be bizarre segmentation faults. (Of course,
// other problems can cause seg faults, so that isn't a sure sign
// that your thread stacks are too small.)
//
// One thing to try if you find yourself with seg faults is to
// increase the size of thread stack -- ThreadStackSize.
//
// In this interface, forking a thread takes two steps.
// We must first allocate a data structure for it: "t = new Thread".
// Only then can we do the fork: "t->fork(f, arg)".
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef THREAD_H
#define THREAD_H
#include "copyright.h"
#include "utility.h"
#include "sysdep.h"
#include "machine.h"
#include "addrspace.h"
// CPU register state to be saved on context switch.
// The x86 needs to save only a few registers,
// SPARC and MIPS needs to save 10 registers,
// the Snake needs 18,
// and the RS6000 needs to save 75 (!)
// For simplicity, I just take the maximum over all architectures.
#define MachineStateSize 75
// Size of the thread's private execution stack.
// WATCH OUT IF THIS ISN'T BIG ENOUGH!!!!!
const int StackSize = (8 * 1024); // in words
// Thread state
enum ThreadStatus { JUST_CREATED, RUNNING, READY, BLOCKED, ZOMBIE };
// The following class defines a "thread control block" -- which
// represents a single thread of execution.
//
// Every thread has:
// an execution stack for activation records ("stackTop" and "stack")
// space to save CPU registers while not running ("machineState")
// a "status" (running/ready/blocked)
//
// Some threads also belong to a user address space; threads
// that only run in the kernel have a NULL address space.
class Thread {
private:
// NOTE: DO NOT CHANGE the order of these first two members.
// THEY MUST be in this position for SWITCH to work.
int *stackTop; // the current stack pointer
void *machineState[MachineStateSize]; // all registers except for stackTop
public:
Thread(char* debugName, int threadID); // initialize a Thread
~Thread(); // deallocate a Thread
// NOTE -- thread being deleted
// must not be running when delete
// is called
// basic thread operations
void Fork(VoidFunctionPtr func, void *arg);
// Make thread run (*func)(arg)
void Yield(); // Relinquish the CPU if any
// other thread is runnable
void Sleep(bool finishing); // Put the thread to sleep and
// relinquish the processor
void Begin(); // Startup code for the thread
void Finish(); // The thread is done executing
void CheckOverflow(); // Check if thread stack has overflowed
void setStatus(ThreadStatus st) { status = st; }
ThreadStatus getStatus() { return (status); }
char* getName() { return (name); }
int getID() { return (ID); }
void Print() { cout << name; }
void SelfTest(); // test whether thread impl is working
private:
// some of the private data for this class is listed above
int *stack; // Bottom of the stack
// NULL if this is the main thread
// (If NULL, don't deallocate stack)
ThreadStatus status; // ready, running or blocked
char* name;
int ID;
void StackAllocate(VoidFunctionPtr func, void *arg);
// Allocate a stack for thread.
// Used internally by Fork()
// A thread running a user program actually has *two* sets of CPU registers --
// one for its state while executing user code, one for its state
// while executing kernel code.
int userRegisters[NumTotalRegs]; // user-level CPU register state
public:
void SaveUserState(); // save user-level register state
void RestoreUserState(); // restore user-level register state
AddrSpace *space; // User code this thread is running.
};
// external function, dummy routine whose sole job is to call Thread::Print
extern void ThreadPrint(Thread *thread);
// Magical machine-dependent routines, defined in switch.s
extern "C" {
// First frame on thread execution stack;
// call ThreadBegin
// call "func"
// (when func returns, if ever) call ThreadFinish()
void ThreadRoot();
// Stop running oldThread and start running newThread
void SWITCH(Thread *oldThread, Thread *newThread);
}
#endif // THREAD_H

316
code/userprog/addrspace.cc Executable file
View File

@@ -0,0 +1,316 @@
// addrspace.cc
// Routines to manage address spaces (executing user programs).
//
// In order to run a user program, you must:
//
// 1. link with the -n -T 0 option
// 2. run coff2noff to convert the object file to Nachos format
// (Nachos object code format is essentially just a simpler
// version of the UNIX executable object code format)
// 3. load the NOFF file into the Nachos file system
// (if you are using the "stub" file system, you
// don't need to do this last step)
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "main.h"
#include "addrspace.h"
#include "machine.h"
#include "noff.h"
//----------------------------------------------------------------------
// SwapHeader
// Do little endian to big endian conversion on the bytes in the
// object file header, in case the file was generated on a little
// endian machine, and we're now running on a big endian machine.
//----------------------------------------------------------------------
static void
SwapHeader (NoffHeader *noffH)
{
noffH->noffMagic = WordToHost(noffH->noffMagic);
noffH->code.size = WordToHost(noffH->code.size);
noffH->code.virtualAddr = WordToHost(noffH->code.virtualAddr);
noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr);
#ifdef RDATA
noffH->readonlyData.size = WordToHost(noffH->readonlyData.size);
noffH->readonlyData.virtualAddr =
WordToHost(noffH->readonlyData.virtualAddr);
noffH->readonlyData.inFileAddr =
WordToHost(noffH->readonlyData.inFileAddr);
#endif
noffH->initData.size = WordToHost(noffH->initData.size);
noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr);
noffH->initData.inFileAddr = WordToHost(noffH->initData.inFileAddr);
noffH->uninitData.size = WordToHost(noffH->uninitData.size);
noffH->uninitData.virtualAddr = WordToHost(noffH->uninitData.virtualAddr);
noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr);
#ifdef RDATA
DEBUG(dbgAddr, "code = " << noffH->code.size <<
" readonly = " << noffH->readonlyData.size <<
" init = " << noffH->initData.size <<
" uninit = " << noffH->uninitData.size << "\n");
#endif
}
//----------------------------------------------------------------------
// AddrSpace::AddrSpace
// Create an address space to run a user program.
// Set up the translation from program memory to physical
// memory. For now, this is really simple (1:1), since we are
// only uniprogramming, and we have a single unsegmented page table
//----------------------------------------------------------------------
AddrSpace::AddrSpace()
{
pageTable = new TranslationEntry[NumPhysPages];
for (int i = 0; i < NumPhysPages; i++) {
pageTable[i].virtualPage = i; // for now, virt page # = phys page #
pageTable[i].physicalPage = i;
pageTable[i].valid = TRUE;
pageTable[i].use = FALSE;
pageTable[i].dirty = FALSE;
pageTable[i].readOnly = FALSE;
}
// zero out the entire address space
bzero(kernel->machine->mainMemory, MemorySize);
}
//----------------------------------------------------------------------
// AddrSpace::~AddrSpace
// Dealloate an address space.
//----------------------------------------------------------------------
AddrSpace::~AddrSpace()
{
delete pageTable;
}
//----------------------------------------------------------------------
// AddrSpace::Load
// Load a user program into memory from a file.
//
// Assumes that the page table has been initialized, and that
// the object code file is in NOFF format.
//
// "fileName" is the file containing the object code to load into memory
//----------------------------------------------------------------------
bool
AddrSpace::Load(char *fileName)
{
OpenFile *executable = kernel->fileSystem->Open(fileName);
NoffHeader noffH;
unsigned int size;
if (executable == NULL) {
cerr << "Unable to open file " << fileName << "\n";
return FALSE;
}
executable->ReadAt((char *)&noffH, sizeof(noffH), 0);
if ((noffH.noffMagic != NOFFMAGIC) &&
(WordToHost(noffH.noffMagic) == NOFFMAGIC))
SwapHeader(&noffH);
ASSERT(noffH.noffMagic == NOFFMAGIC);
#ifdef RDATA
// how big is address space?
size = noffH.code.size + noffH.readonlyData.size + noffH.initData.size +
noffH.uninitData.size + UserStackSize;
// we need to increase the size
// to leave room for the stack
#else
// how big is address space?
size = noffH.code.size + noffH.initData.size + noffH.uninitData.size
+ UserStackSize; // we need to increase the size
// to leave room for the stack
#endif
numPages = divRoundUp(size, PageSize);
size = numPages * PageSize;
ASSERT(numPages <= NumPhysPages); // check we're not trying
// to run anything too big --
// at least until we have
// virtual memory
DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size);
// then, copy in the code and data segments into memory
// Note: this code assumes that virtual address = physical address
if (noffH.code.size > 0) {
DEBUG(dbgAddr, "Initializing code segment.");
DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.code.virtualAddr]),
noffH.code.size, noffH.code.inFileAddr);
}
if (noffH.initData.size > 0) {
DEBUG(dbgAddr, "Initializing data segment.");
DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.initData.virtualAddr]),
noffH.initData.size, noffH.initData.inFileAddr);
}
#ifdef RDATA
if (noffH.readonlyData.size > 0) {
DEBUG(dbgAddr, "Initializing read only data segment.");
DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.readonlyData.virtualAddr]),
noffH.readonlyData.size, noffH.readonlyData.inFileAddr);
}
#endif
delete executable; // close file
return TRUE; // success
}
//----------------------------------------------------------------------
// AddrSpace::Execute
// Run a user program using the current thread
//
// The program is assumed to have already been loaded into
// the address space
//
//----------------------------------------------------------------------
void
AddrSpace::Execute(char* fileName)
{
kernel->currentThread->space = this;
this->InitRegisters(); // set the initial register values
this->RestoreState(); // load page table register
kernel->machine->Run(); // jump to the user progam
ASSERTNOTREACHED(); // machine->Run never returns;
// the address space exits
// by doing the syscall "exit"
}
//----------------------------------------------------------------------
// AddrSpace::InitRegisters
// Set the initial values for the user-level register set.
//
// We write these directly into the "machine" registers, so
// that we can immediately jump to user code. Note that these
// will be saved/restored into the currentThread->userRegisters
// when this thread is context switched out.
//----------------------------------------------------------------------
void
AddrSpace::InitRegisters()
{
Machine *machine = kernel->machine;
int i;
for (i = 0; i < NumTotalRegs; i++)
machine->WriteRegister(i, 0);
// Initial program counter -- must be location of "Start", which
// is assumed to be virtual address zero
machine->WriteRegister(PCReg, 0);
// Need to also tell MIPS where next instruction is, because
// of branch delay possibility
// Since instructions occupy four bytes each, the next instruction
// after start will be at virtual address four.
machine->WriteRegister(NextPCReg, 4);
// Set the stack register to the end of the address space, where we
// allocated the stack; but subtract off a bit, to make sure we don't
// accidentally reference off the end!
machine->WriteRegister(StackReg, numPages * PageSize - 16);
DEBUG(dbgAddr, "Initializing stack pointer: " << numPages * PageSize - 16);
}
//----------------------------------------------------------------------
// AddrSpace::SaveState
// On a context switch, save any machine state, specific
// to this address space, that needs saving.
//
// For now, don't need to save anything!
//----------------------------------------------------------------------
void AddrSpace::SaveState()
{}
//----------------------------------------------------------------------
// AddrSpace::RestoreState
// On a context switch, restore the machine state so that
// this address space can run.
//
// For now, tell the machine where to find the page table.
//----------------------------------------------------------------------
void AddrSpace::RestoreState()
{
kernel->machine->pageTable = pageTable;
kernel->machine->pageTableSize = numPages;
}
//----------------------------------------------------------------------
// AddrSpace::Translate
// Translate the virtual address in _vaddr_ to a physical address
// and store the physical address in _paddr_.
// The flag _isReadWrite_ is false (0) for read-only access; true (1)
// for read-write access.
// Return any exceptions caused by the address translation.
//----------------------------------------------------------------------
ExceptionType
AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
{
TranslationEntry *pte;
int pfn;
unsigned int vpn = vaddr / PageSize;
unsigned int offset = vaddr % PageSize;
if(vpn >= numPages) {
return AddressErrorException;
}
pte = &pageTable[vpn];
if(isReadWrite && pte->readOnly) {
return ReadOnlyException;
}
pfn = pte->physicalPage;
// if the pageFrame is too big, there is something really wrong!
// An invalid translation was loaded into the page table or TLB.
if (pfn >= NumPhysPages) {
DEBUG(dbgAddr, "Illegal physical page " << pfn);
return BusErrorException;
}
pte->use = TRUE; // set the use, dirty bits
if(isReadWrite)
pte->dirty = TRUE;
*paddr = pfn*PageSize + offset;
ASSERT((*paddr < MemorySize));
//cerr << " -- AddrSpace::Translate(): vaddr: " << vaddr <<
// ", paddr: " << *paddr << "\n";
return NoException;
}

53
code/userprog/addrspace.h Executable file
View File

@@ -0,0 +1,53 @@
// addrspace.h
// Data structures to keep track of executing user programs
// (address spaces).
//
// For now, we don't keep any information about address spaces.
// The user level CPU state is saved and restored in the thread
// executing the user program (see thread.h).
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef ADDRSPACE_H
#define ADDRSPACE_H
#include "copyright.h"
#include "filesys.h"
#define UserStackSize 1024 // increase this as necessary!
class AddrSpace {
public:
AddrSpace(); // Create an address space.
~AddrSpace(); // De-allocate an address space
bool Load(char *fileName); // Load a program into addr space from
// a file
// return false if not found
void Execute(char *fileName); // Run a program
// assumes the program has already
// been loaded
void SaveState(); // Save/restore address space-specific
void RestoreState(); // info on a context switch
// Translate virtual address _vaddr_
// to physical address _paddr_. _mode_
// is 0 for Read, 1 for Write.
ExceptionType Translate(unsigned int vaddr, unsigned int *paddr, int mode);
private:
TranslationEntry *pageTable; // Assume linear page table translation
// for now!
unsigned int numPages; // Number of pages in the virtual
// address space
void InitRegisters(); // Initialize user-level CPU registers,
// before jumping to user code
};
#endif // ADDRSPACE_H

84
code/userprog/errno.h Executable file
View File

@@ -0,0 +1,84 @@
/* errno.h
* Error codes for system calls.
*
* Do not modify the contents of this file.
*
* Try to use the most descriptive error number for the error.
* You may find it helpful to see what errors equivalent UNIX
* system calls return under various error conditions.
* For example the man page for the write system call "man 2 write"
* provides a list of different error number values for different
* conditions.
* NOTE: there are way more errors here than you should be supporting
* and many more error conditions listed in man pages than
* Nachos can possibly generate. The list here is to give you
* some ideas and to hopefully standardize on some error numbers.
*
* ALSO NOTE: These definitions may not correspond to Solaris definitions
* (for copyright reasons these are taken from Linux).
*/
#ifndef ERRNO_H
#define ERRNO_H
#include "copyright.h"
#define EPERM -1 /* Operation not permitted */
#define ENOENT -2 /* No such file or directory */
#define ESRCH -3 /* No such process */
#define EINTR -4 /* Interrupted system call */
#define EIO -5 /* I/O error */
#define ENXIO -6 /* No such device or address */
#define E2BIG -7 /* Arg list too long */
#define ENOEXEC -8 /* Exec format error */
#define EBADF -9 /* Bad file number */
#define ECHILD -10 /* No child processes */
#define EAGAIN -11 /* Try again */
#define ENOMEM -12 /* Out of memory */
#define EACCES -13 /* Permission denied */
#define EFAULT -14 /* Bad address */
#define ENOTBLK -15 /* Block device required */
#define EBUSY -16 /* Device or resource busy */
#define EEXIST -17 /* File exists */
#define EXDEV -18 /* Cross-device link */
#define ENODEV -19 /* No such device */
#define ENOTDIR -20 /* Not a directory */
#define EISDIR -21 /* Is a directory */
#define EINVAL -22 /* Invalid argument */
#define ENFILE -23 /* File table overflow */
#define EMFILE -24 /* Too many open files */
#define ENOTTY -25 /* Not a typewriter */
#define ETXTBSY -26 /* Text file busy */
#define EFBIG -27 /* File too large */
#define ENOSPC -28 /* No space left on device */
#define ESPIPE -29 /* Illegal seek */
#define EROFS -30 /* Read-only file system */
#define EMLINK -31 /* Too many links */
#define EPIPE -32 /* Broken pipe */
#define EDOM -33 /* Math argument out of domain of func */
#define ERANGE -34 /* Math result not representable */
#define EDEADLK -35 /* Resource deadlock would occur */
#define ENAMETOOLONG -36 /* File name too long */
#define ENOLCK -37 /* No record locks available */
#define ENOSYS -38 /* Function not implemented */
#define ENOTEMPTY -39 /* Directory not empty */
#define ELOOP -40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG -42 /* No message of desired type */
#define EIDRM -43 /* Identifier removed */
#define ECHRNG -44 /* Channel number out of range */
#define EL2NSYNC -45 /* Level 2 not synchronized */
#define EL3HLT -46 /* Level 3 halted */
#define EL3RST -47 /* Level 3 reset */
#define ELNRNG -48 /* Link number out of range */
#define EUNATCH -49 /* Protocol driver not attached */
#define ENOCSI -50 /* No CSI structure available */
#define EL2HLT -51 /* Level 2 halted */
#define EBADE -52 /* Invalid exchange */
#define EBADR -53 /* Invalid request descriptor */
#define EXFULL -54 /* Exchange full */
#define ENOANO -55 /* No anode */
#define EBADRQC -56 /* Invalid request code */
#define EBADSLT -57 /* Invalid slot */
#endif // ERRNO_H

132
code/userprog/exception.cc Executable file
View File

@@ -0,0 +1,132 @@
// exception.cc
// Entry point into the Nachos kernel from user programs.
// There are two kinds of things that can cause control to
// transfer back to here from user code:
//
// syscall -- The user code explicitly requests to call a procedure
// in the Nachos kernel. Right now, the only function we support is
// "Halt".
//
// exceptions -- The user code does something that the CPU can't handle.
// For instance, accessing memory that doesn't exist, arithmetic errors,
// etc.
//
// Interrupts (which can also cause control to transfer from user
// code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "main.h"
#include "syscall.h"
#include "ksyscall.h"
//----------------------------------------------------------------------
// ExceptionHandler
// Entry point into the Nachos kernel. Called when a user program
// is executing, and either does a syscall, or generates an addressing
// or arithmetic exception.
//
// For system calls, the following is the calling convention:
//
// system call code -- r2
// arg1 -- r4
// arg2 -- r5
// arg3 -- r6
// arg4 -- r7
//
// The result of the system call, if any, must be put back into r2.
//
// If you are handling a system call, don't forget to increment the pc
// before returning. (Or else you'll loop making the same system call forever!)
//
// "which" is the kind of exception. The list of possible exceptions
// is in machine.h.
//----------------------------------------------------------------------
void
ExceptionHandler(ExceptionType which)
{
int type = kernel->machine->ReadRegister(2);
int val;
int status, exit, threadID, programID;
DEBUG(dbgSys, "Received Exception " << which << " type: " << type << "\n");
switch (which) {
case SyscallException:
switch(type) {
case SC_Halt:
DEBUG(dbgSys, "Shutdown, initiated by user program.\n");
SysHalt();
cout<<"in exception\n";
ASSERTNOTREACHED();
break;
case SC_MSG:
DEBUG(dbgSys, "Message received.\n");
val = kernel->machine->ReadRegister(4);
{
char *msg = &(kernel->machine->mainMemory[val]);
cout << msg << endl;
}
SysHalt();
ASSERTNOTREACHED();
break;
case SC_Create:
val = kernel->machine->ReadRegister(4);
{
char *filename = &(kernel->machine->mainMemory[val]);
//cout << filename << endl;
status = SysCreate(filename);
kernel->machine->WriteRegister(2, (int) status);
}
kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg)+4);
return;
ASSERTNOTREACHED();
break;
case SC_Add:
DEBUG(dbgSys, "Add " << kernel->machine->ReadRegister(4) << " + " << kernel->machine->ReadRegister(5) << "\n");
/* Process SysAdd Systemcall*/
int result;
result = SysAdd(/* int op1 */(int)kernel->machine->ReadRegister(4),
/* int op2 */(int)kernel->machine->ReadRegister(5));
DEBUG(dbgSys, "Add returning with " << result << "\n");
/* Prepare Result */
kernel->machine->WriteRegister(2, (int)result);
/* Modify return point */
{
/* set previous programm counter (debugging only)*/
kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
/* set programm counter to next instruction (all Instructions are 4 byte wide)*/
kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
/* set next programm counter for brach execution */
kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg)+4);
}
cout << "result is " << result << "\n";
return;
ASSERTNOTREACHED();
break;
case SC_Exit:
DEBUG(dbgAddr, "Program exit\n");
val=kernel->machine->ReadRegister(4);
cout << "return value:" << val << endl;
kernel->currentThread->Finish();
break;
default:
cerr << "Unexpected system call " << type << "\n";
break;
}
break;
default:
cerr << "Unexpected user mode exception " << (int)which << "\n";
break;
}
ASSERTNOTREACHED();
}

38
code/userprog/ksyscall.h Executable file
View File

@@ -0,0 +1,38 @@
/**************************************************************
*
* userprog/ksyscall.h
*
* Kernel interface for systemcalls
*
* by Marcus Voelp (c) Universitaet Karlsruhe
*
**************************************************************/
#ifndef __USERPROG_KSYSCALL_H__
#define __USERPROG_KSYSCALL_H__
#include "kernel.h"
#include "synchconsole.h"
void SysHalt()
{
kernel->interrupt->Halt();
}
int SysAdd(int op1, int op2)
{
return op1 + op2;
}
int SysCreate(char *filename)
{
// return value
// 1: success
// 0: failed
return kernel->interrupt->CreateFile(filename);
}
#endif /* ! __USERPROG_KSYSCALL_H__ */

28
code/userprog/noff.h Executable file
View File

@@ -0,0 +1,28 @@
/* noff.h
* Data structures defining the Nachos Object Code Format
*
* Basically, we only know about three types of segments:
* code (read-only), initialized data, and unitialized data
*/
#define NOFFMAGIC 0xbadfad /* magic number denoting Nachos
* object code file
*/
typedef struct segment {
int virtualAddr; /* location of segment in virt addr space */
int inFileAddr; /* location of segment in this file */
int size; /* size of segment */
} Segment;
typedef struct noffHeader {
int noffMagic; /* should be NOFFMAGIC */
Segment code; /* executable code segment */
Segment initData; /* initialized data segment */
#ifdef RDATA
Segment readonlyData; /* read only data */
#endif
Segment uninitData; /* uninitialized data segment --
* should be zero'ed before use
*/
} NoffHeader;

119
code/userprog/synchconsole.cc Executable file
View File

@@ -0,0 +1,119 @@
// synchconsole.cc
// Routines providing synchronized access to the keyboard
// and console display hardware devices.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#include "copyright.h"
#include "synchconsole.h"
//----------------------------------------------------------------------
// SynchConsoleInput::SynchConsoleInput
// Initialize synchronized access to the keyboard
//
// "inputFile" -- if NULL, use stdin as console device
// otherwise, read from this file
//----------------------------------------------------------------------
SynchConsoleInput::SynchConsoleInput(char *inputFile)
{
consoleInput = new ConsoleInput(inputFile, this);
lock = new Lock("console in");
waitFor = new Semaphore("console in", 0);
}
//----------------------------------------------------------------------
// SynchConsoleInput::~SynchConsoleInput
// Deallocate data structures for synchronized access to the keyboard
//----------------------------------------------------------------------
SynchConsoleInput::~SynchConsoleInput()
{
delete consoleInput;
delete lock;
delete waitFor;
}
//----------------------------------------------------------------------
// SynchConsoleInput::GetChar
// Read a character typed at the keyboard, waiting if necessary.
//----------------------------------------------------------------------
char
SynchConsoleInput::GetChar()
{
char ch;
lock->Acquire();
waitFor->P(); // wait for EOF or a char to be available.
ch = consoleInput->GetChar();
lock->Release();
return ch;
}
//----------------------------------------------------------------------
// SynchConsoleInput::CallBack
// Interrupt handler called when keystroke is hit; wake up
// anyone waiting.
//----------------------------------------------------------------------
void
SynchConsoleInput::CallBack()
{
waitFor->V();
}
//----------------------------------------------------------------------
// SynchConsoleOutput::SynchConsoleOutput
// Initialize synchronized access to the console display
//
// "outputFile" -- if NULL, use stdout as console device
// otherwise, read from this file
//----------------------------------------------------------------------
SynchConsoleOutput::SynchConsoleOutput(char *outputFile)
{
consoleOutput = new ConsoleOutput(outputFile, this);
lock = new Lock("console out");
waitFor = new Semaphore("console out", 0);
}
//----------------------------------------------------------------------
// SynchConsoleOutput::~SynchConsoleOutput
// Deallocate data structures for synchronized access to the keyboard
//----------------------------------------------------------------------
SynchConsoleOutput::~SynchConsoleOutput()
{
delete consoleOutput;
delete lock;
delete waitFor;
}
//----------------------------------------------------------------------
// SynchConsoleOutput::PutChar
// Write a character to the console display, waiting if necessary.
//----------------------------------------------------------------------
void
SynchConsoleOutput::PutChar(char ch)
{
lock->Acquire();
consoleOutput->PutChar(ch);
waitFor->P();
lock->Release();
}
//----------------------------------------------------------------------
// SynchConsoleOutput::CallBack
// Interrupt handler called when it's safe to send the next
// character can be sent to the display.
//----------------------------------------------------------------------
void
SynchConsoleOutput::CallBack()
{
waitFor->V();
}

53
code/userprog/synchconsole.h Executable file
View File

@@ -0,0 +1,53 @@
// synchconsole.h
// Data structures for synchronized access to the keyboard
// and console display devices.
//
// NOTE: this abstraction is not completely implemented.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions.
#ifndef SYNCHCONSOLE_H
#define SYNCHCONSOLE_H
#include "copyright.h"
#include "utility.h"
#include "callback.h"
#include "console.h"
#include "synch.h"
// The following two classes define synchronized input and output to
// a console device
class SynchConsoleInput : public CallBackObj {
public:
SynchConsoleInput(char *inputFile); // Initialize the console device
~SynchConsoleInput(); // Deallocate console device
char GetChar(); // Read a character, waiting if necessary
private:
ConsoleInput *consoleInput; // the hardware keyboard
Lock *lock; // only one reader at a time
Semaphore *waitFor; // wait for callBack
void CallBack(); // called when a keystroke is available
};
class SynchConsoleOutput : public CallBackObj {
public:
SynchConsoleOutput(char *outputFile); // Initialize the console device
~SynchConsoleOutput();
void PutChar(char ch); // Write a character, waiting if necessary
private:
ConsoleOutput *consoleOutput;// the hardware display
Lock *lock; // only one writer at a time
Semaphore *waitFor; // wait for callBack
void CallBack(); // called when more data can be written
};
#endif // SYNCHCONSOLE_H

183
code/userprog/syscall.h Executable file
View File

@@ -0,0 +1,183 @@
/* syscalls.h
* Nachos system call interface. These are Nachos kernel operations
* that can be invoked from user programs, by trapping to the kernel
* via the "syscall" instruction.
*
* This file is included by user programs and by the Nachos kernel.
*
* Copyright (c) 1992-1993 The Regents of the University of California.
* All rights reserved. See copyright.h for copyright notice and limitation
* of liability and disclaimer of warranty provisions.
*/
#ifndef SYSCALLS_H
#define SYSCALLS_H
#include "copyright.h"
#include "errno.h"
/* system call codes -- used by the stubs to tell the kernel which system call
* is being asked for
*/
#define SC_Halt 0
#define SC_Exit 1
#define SC_Exec 2
#define SC_Join 3
#define SC_Create 4
#define SC_Remove 5
#define SC_Open 6
#define SC_Read 7
#define SC_Write 8
#define SC_Seek 9
#define SC_Close 10
#define SC_ThreadFork 11
#define SC_ThreadYield 12
#define SC_ExecV 13
#define SC_ThreadExit 14
#define SC_ThreadJoin 15
#define SC_Add 42
#define SC_MSG 100
#ifndef IN_ASM
/* The system call interface. These are the operations the Nachos
* kernel needs to support, to be able to run user programs.
*
* Each of these is invoked by a user program by simply calling the
* procedure; an assembly language stub stuffs the system call code
* into a register, and traps to the kernel. The kernel procedures
* are then invoked in the Nachos kernel, after appropriate error checking,
* from the system call entry point in exception.cc.
*/
/* Stop Nachos, and print out performance stats */
void Halt();
/*
* Add the two operants and return the result
*/
int Add(int op1, int op2);
/*
* Just for simply showing message, not a safe way for console IO
*/
void MSG(char *msg);
/* Address space control operations: Exit, Exec, Execv, and Join */
/* This user program is done (status = 0 means exited normally). */
void Exit(int status);
/* A unique identifier for an executing user program (address space) */
typedef int SpaceId;
/* A unique identifier for a thread within a task */
typedef int ThreadId;
/* Run the specified executable, with no args */
/* This can be implemented as a call to ExecV.
*/
SpaceId Exec(char* exec_name);
/* Run the executable, stored in the Nachos file "argv[0]", with
* parameters stored in argv[1..argc-1] and return the
* address space identifier
*/
SpaceId ExecV(int argc, char* argv[]);
/* Only return once the user program "id" has finished.
* Return the exit status.
*/
int Join(SpaceId id);
/* File system operations: Create, Remove, Open, Read, Write, Close
* These functions are patterned after UNIX -- files represent
* both files *and* hardware I/O devices.
*
* Note that the Nachos file system has a stub implementation, which
* can be used to support these system calls if the regular Nachos
* file system has not been implemented.
*/
/* A unique identifier for an open Nachos file. */
typedef int OpenFileId;
/* when an address space starts up, it has two open files, representing
* keyboard input and display output (in UNIX terms, stdin and stdout).
* Read and Write can be used directly on these, without first opening
* the console device.
*/
#define SysConsoleInput 0
#define SysConsoleOutput 1
/* Create a Nachos file, with name "name" */
/* Note: Create does not open the file. */
/* Return 1 on success, negative error code on failure */
int Create(char *name);
/* Remove a Nachos file, with name "name" */
int Remove(char *name);
/* Open the Nachos file "name", and return an "OpenFileId" that can
* be used to read and write to the file.
*/
OpenFileId Open(char *name);
/* Write "size" bytes from "buffer" to the open file.
* Return the number of bytes actually read on success.
* On failure, a negative error code is returned.
*/
int Write(char *buffer, int size, OpenFileId id);
/* Read "size" bytes from the open file into "buffer".
* Return the number of bytes actually read -- if the open file isn't
* long enough, or if it is an I/O device, and there aren't enough
* characters to read, return whatever is available (for I/O devices,
* you should always wait until you can return at least one character).
*/
int Read(char *buffer, int size, OpenFileId id);
/* Set the seek position of the open file "id"
* to the byte "position".
*/
int Seek(int position, OpenFileId id);
/* Close the file, we're done reading and writing to it.
* Return 1 on success, negative error code on failure
*/
int Close(OpenFileId id);
/* User-level thread operations: Fork and Yield. To allow multiple
* threads to run within a user program.
*
* Could define other operations, such as LockAcquire, LockRelease, etc.
*/
/* Fork a thread to run a procedure ("func") in the *same* address space
* as the current thread.
* Return a positive ThreadId on success, negative error code on failure
*/
ThreadId ThreadFork(void (*func)());
/* Yield the CPU to another runnable thread, whether in this address space
* or not.
*/
void ThreadYield();
/*
* Blocks current thread until lokal thread ThreadID exits with ThreadExit.
* Function returns the ExitCode of ThreadExit() of the exiting thread.
*/
int ThreadJoin(ThreadId id);
/*
* Deletes current thread and returns ExitCode to every waiting lokal thread.
*/
void ThreadExit(int ExitCode);
#endif /* IN_ASM */
#endif /* SYSCALL_H */