Compare commits

..

10 Commits

Author SHA1 Message Date
72320ede22 hw4 test 2024-12-30 05:59:42 +08:00
bf78b95c9d Remove: sanitizer flag in compile and link 2024-11-02 08:11:33 +08:00
5f06249b01 Fix: Custom test CI script 2024-11-02 08:04:02 +08:00
Yi-Ting Shih
b4987f1f70 Merge branch 'ytshih/hw2' into 'main'
Ytshih/hw2

See merge request cs_os_group_20/cs_os_project_20_hw!2
2024-11-02 07:58:12 +08:00
Yi-Ting Shih
4912fe4736 Ytshih/hw2 2024-11-02 07:58:12 +08:00
ChenYen-Yen
549bc9bcdc merge update 2024-10-22 16:29:46 +08:00
ChenYen-Yen
b18dbf056f update PrintInt and change PutString to PutInt 2024-10-22 16:27:49 +08:00
5b1cd5e1cf Add: README
Fix: newline when PrintInt outputs 0
2024-10-05 04:13:43 +08:00
施羿廷
486f032cf0 Merge branch 'ytshih-hw1' into 'main'
HW1

See merge request cs_os_group_20/cs_os_project_20_hw!1
2024-10-04 19:53:08 +00:00
施羿廷
ba9ef819ba HW1 2024-10-04 19:53:08 +00:00
86 changed files with 1510 additions and 4608 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.o
*.coff

16
Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
FROM ubuntu:22.04
RUN dpkg --add-architecture i386
RUN apt-get update && apt-get dist-upgrade
RUN apt-get -y install build-essential ed \
gcc-multilib g++-multilib lib32ncurses5-dev lib32z1 \
zlib1g:i386 libstdc++6:i386 libc6:i386 libncurses5:i386 \
libgcc1:i386 libstdc++5:i386
RUN apt-get -y install fish vim less gdb
RUN groupadd -g 60139 ytshih && useradd -g 60139 -u 60139 ytshih
WORKDIR /work
ENTRYPOINT ["/usr/bin/env"]
CMD ["fish"]

17
README.md Normal file
View File

@@ -0,0 +1,17 @@
# Intro. to OS HW1
## Docker Compose usage
Install docker and docker-compose if not installed.
1. Change uid / gid to yours in `Dockerfile` and `docker-compose.yaml`.
2. Run `docker compose build` to build Docker image.
3. Run `docker compose run test` to launch testing environment.
## Makefile
First, `cd` into `code` directory.
- `make clean` to clean previous build.
- `make` to build.
- `make run` to run tests.

17
code/Makefile Normal file
View File

@@ -0,0 +1,17 @@
.PHONY: all clean run
all:
make -C build.linux depend
make -C build.linux -j 16
make -C test -j 16
clean:
make -C build.linux distclean
make -C test distclean
run:
make -C test run
debug:
make -C test debug

Binary file not shown.

View File

@@ -200,18 +200,18 @@ DEFINES = -DFILESYS_STUB -DRDATA -DSIM_FIX
# break the thread system. You might want to use -fno-inline if # break the thread system. You might want to use -fno-inline if
# you need to call some inline functions from the debugger. # you need to call some inline functions from the debugger.
CFLAGS = -g -Wall $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -m32 CFLAGS = -g -Wall $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -m32 -w
LDFLAGS = -m32 LDFLAGS = -m32
CPP_AS_FLAGS= -m32 CPP_AS_FLAGS= -m32
##################################################################### #####################################################################
CPP=/lib/cpp CPP=/lib/cpp
CC = g++ -m32 -Wno-deprecated CC = g++ -m32
LD = g++ -m32 -Wno-deprecated LD = g++ -m32
AS = as --32 AS = as --32
RM = /bin/rm RM = /bin/rm
INCPATH = -I../network -I../filesys -I../userprog -I../threads -I../machine -I../lib -I- INCPATH = -iquote../network -iquote../filesys -iquote../userprog -iquote../threads -iquote../machine -iquote../lib
PROGRAM = nachos PROGRAM = nachos

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -40,57 +40,108 @@
#ifdef FILESYS_STUB // Temporarily implement file system calls as #ifdef FILESYS_STUB // Temporarily implement file system calls as
// calls to UNIX, until the real file system // calls to UNIX, until the real file system
// implementation is available // implementation is available
typedef int OpenFileId;
class FileSystem { class FileSystem {
public: public:
FileSystem() { for (int i = 0; i < 20; i++) fileDescriptorTable[i] = NULL; } FileSystem() { for (int i = 0; i < 20; i++) fileDescriptorTable[i] = NULL; }
bool Create(char *name) { bool Create(char* name) {
int fileDescriptor = OpenForWrite(name); int fileDescriptor = OpenForWrite(name);
if (fileDescriptor == -1) return FALSE;
if (fileDescriptor == -1) return FALSE; Close(fileDescriptor);
Close(fileDescriptor); return TRUE;
return TRUE;
} }
OpenFile* Open(char *name) { OpenFile* Open(char* name) {
int fileDescriptor = OpenForReadWrite(name, FALSE); int fileDescriptor = OpenForReadWrite(name, FALSE);
if (fileDescriptor == -1) return NULL;
return new OpenFile(fileDescriptor);
}
if (fileDescriptor == -1) return NULL; OpenFileId OpenFiles(char* name) {
return new OpenFile(fileDescriptor); OpenFile* file = Open(name);
} if (!file) return -1;
int freeIndex = -1;
bool Remove(char *name) { return Unlink(name) == 0; } for (int i = 0; i < 20; i++)
if (!fileDescriptorTable[i])
freeIndex = i;
if (freeIndex == -1)
return -1;
OpenFileId fileDescriptor = file->GetFileDescriptor();
fileDescriptorTable[freeIndex] = file;
return fileDescriptor;
}
int WriteFile(char* buffer, int size, OpenFileId fd) {
for (int i = 0; i < 20; i++) {
if (!fileDescriptorTable[i])
continue;
if (fileDescriptorTable[i]->GetFileDescriptor() == fd) {
return fileDescriptorTable[i]->Write(buffer, size);
}
}
return -1;
}
int ReadFile(char* buffer, int size, OpenFileId fd) {
for (int i = 0; i < 20; i++) {
if (!fileDescriptorTable[i])
continue;
if (fileDescriptorTable[i]->GetFileDescriptor() == fd)
return fileDescriptorTable[i]->Read(buffer, size);
}
return -1;
}
int CloseFile(OpenFileId fd) {
for (int i = 0; i < 20; i++) {
if (!fileDescriptorTable[i])
continue;
if (fileDescriptorTable[i]->GetFileDescriptor() == fd) {
delete fileDescriptorTable[i];
fileDescriptorTable[i] = NULL;
return 1;
}
}
return 0;
}
bool Remove(char* name) { return Unlink(name) == 0; }
OpenFile* fileDescriptorTable[20];
OpenFile *fileDescriptorTable[20];
}; };
#else // FILESYS #else // FILESYS
class FileSystem { class FileSystem {
public: public:
FileSystem(bool format); // Initialize the file system. FileSystem(bool format); // Initialize the file system.
// Must be called *after* "synchDisk" // Must be called *after* "synchDisk"
// has been initialized. // has been initialized.
// If "format", there is nothing on // If "format", there is nothing on
// the disk, so initialize the directory // the disk, so initialize the directory
// and the bitmap of free blocks. // and the bitmap of free blocks.
bool Create(char *name, int initialSize); bool Create(char* name, int initialSize);
// Create a file (UNIX creat) // Create a file (UNIX creat)
OpenFile* Open(char *name); // Open a file (UNIX open) OpenFile* Open(char* name); // Open a file (UNIX open)
bool Remove(char *name); // Delete a file (UNIX unlink) bool Remove(char* name); // Delete a file (UNIX unlink)
void List(); // List all the files in the file system void List(); // List all the files in the file system
void Print(); // List all the files and their contents void Print(); // List all the files and their contents
private: private:
OpenFile* freeMapFile; // Bit map of free disk blocks, OpenFile* freeMapFile; // Bit map of free disk blocks,
// represented as a file // represented as a file
OpenFile* directoryFile; // "Root" directory -- list of OpenFile* directoryFile; // "Root" directory -- list of
// file names, represented as a file // file names, represented as a file
}; };
#endif // FILESYS #endif // FILESYS

View File

@@ -27,6 +27,8 @@
#ifdef FILESYS_STUB // Temporarily implement calls to #ifdef FILESYS_STUB // Temporarily implement calls to
// Nachos file system as calls to UNIX! // Nachos file system as calls to UNIX!
// See definitions listed under #else // See definitions listed under #else
typedef int OpenFileId;
class OpenFile { class OpenFile {
public: public:
OpenFile(int f) { file = f; currentOffset = 0; } // open the file OpenFile(int f) { file = f; currentOffset = 0; } // open the file
@@ -53,6 +55,10 @@ class OpenFile {
} }
int Length() { Lseek(file, 0, 2); return Tell(file); } int Length() { Lseek(file, 0, 2); return Tell(file); }
OpenFileId GetFileDescriptor() {
return file;
}
private: private:
int file; int file;

View File

@@ -29,6 +29,7 @@ const char dbgFile = 'f'; // file system
const char dbgAddr = 'a'; // address spaces const char dbgAddr = 'a'; // address spaces
const char dbgNet = 'n'; // network emulation const char dbgNet = 'n'; // network emulation
const char dbgSys = 'u'; // systemcall const char dbgSys = 'u'; // systemcall
const char dbgSche = 'z';
class Debug { class Debug {
public: public:

View File

@@ -172,3 +172,19 @@ ConsoleOutput::PutChar(char ch)
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleWriteInt); kernel->interrupt->Schedule(this, ConsoleTime, ConsoleWriteInt);
} }
//----------------------------------------------------------------------
// ConsoleOutput::PutInt()
// Write a int to the simulated display, schedule an interrupt
// to occur in the future, and return.
//----------------------------------------------------------------------
void
ConsoleOutput::PutInt(int value)
{
ASSERT(putBusy == FALSE);
char * printStr = (char*)malloc(sizeof(char) * 15);
sprintf(printStr, "%d\n", value);
WriteFile(writeFileNo, printStr, strlen(printStr) * sizeof(char));
putBusy = TRUE;
kernel->interrupt->Schedule(this, ConsoleTime, ConsoleWriteInt);
}

View File

@@ -76,6 +76,9 @@ class ConsoleOutput : public CallBackObj {
void PutChar(char ch); // Write "ch" to the console display, void PutChar(char ch); // Write "ch" to the console display,
// and return immediately. "callWhenDone" // and return immediately. "callWhenDone"
// will called when the I/O completes. // will called when the I/O completes.
void PutInt(int value);
void CallBack(); // Invoked when next character can be put void CallBack(); // Invoked when next character can be put
// out to the display. // out to the display.

View File

@@ -22,6 +22,7 @@
#include "copyright.h" #include "copyright.h"
#include "interrupt.h" #include "interrupt.h"
#include "synchconsole.h"
#include "main.h" #include "main.h"
// String definitions for debugging messages // String definitions for debugging messages
@@ -359,3 +360,31 @@ Interrupt::DumpState()
cout << "\nEnd of pending interrupts\n"; cout << "\nEnd of pending interrupts\n";
} }
void Interrupt::PrintInt(int value)
{
kernel->synchConsoleOut->PutInt(value);
}
OpenFileId
Interrupt::OpenFile(char *filename)
{
return kernel->fileSystem->OpenFiles(filename);
}
int
Interrupt::WriteFile(char *buffer, int size, OpenFileId fd)
{
return kernel->fileSystem->WriteFile(buffer, size, fd);
}
int
Interrupt::CloseFile(OpenFileId fd)
{
return kernel->fileSystem->CloseFile(fd);
}
int
Interrupt::ReadFile(char *buffer, int size, OpenFileId fd)
{
return kernel->fileSystem->ReadFile(buffer, size, fd);
}

View File

@@ -37,35 +37,41 @@
#include "list.h" #include "list.h"
#include "callback.h" #include "callback.h"
#include "filesys.h"
typedef int OpenFileId;
// Interrupts can be disabled (IntOff) or enabled (IntOn) // Interrupts can be disabled (IntOff) or enabled (IntOn)
enum IntStatus { IntOff, IntOn }; enum IntStatus { IntOff, IntOn };
// Nachos can be running kernel code (SystemMode), user code (UserMode), // Nachos can be running kernel code (SystemMode), user code (UserMode),
// or there can be no runnable thread, because the ready list // or there can be no runnable thread, because the ready list
// is empty (IdleMode). // is empty (IdleMode).
enum MachineStatus {IdleMode, SystemMode, UserMode}; enum MachineStatus { IdleMode, SystemMode, UserMode };
// IntType records which hardware device generated an interrupt. // IntType records which hardware device generated an interrupt.
// In Nachos, we support a hardware timer device, a disk, a console // In Nachos, we support a hardware timer device, a disk, a console
// display and keyboard, and a network. // display and keyboard, and a network.
enum IntType { TimerInt, DiskInt, ConsoleWriteInt, ConsoleReadInt, enum IntType {
NetworkSendInt, NetworkRecvInt}; TimerInt, DiskInt, ConsoleWriteInt, ConsoleReadInt,
NetworkSendInt, NetworkRecvInt
};
// The following class defines an interrupt that is scheduled // The following class defines an interrupt that is scheduled
// to occur in the future. The internal data structures are // to occur in the future. The internal data structures are
// left public to make it simpler to manipulate. // left public to make it simpler to manipulate.
class PendingInterrupt { class PendingInterrupt {
public: public:
PendingInterrupt(CallBackObj *callOnInt, int time, IntType kind); PendingInterrupt(CallBackObj* callOnInt, int time, IntType kind);
// initialize an interrupt that will // initialize an interrupt that will
// occur in the future // occur in the future
CallBackObj *callOnInterrupt;// The object (in the hardware device CallBackObj* callOnInterrupt;// The object (in the hardware device
// emulator) to call when the interrupt occurs // emulator) to call when the interrupt occurs
int when; // When the interrupt is supposed to fire int when; // When the interrupt is supposed to fire
IntType type; // for debugging IntType type; // for debugging
}; };
// The following class defines the data structures for the simulation // The following class defines the data structures for the simulation
@@ -74,72 +80,76 @@ class PendingInterrupt {
// in the future. // in the future.
class Interrupt { class Interrupt {
public: public:
Interrupt(); // initialize the interrupt simulation Interrupt(); // initialize the interrupt simulation
~Interrupt(); // de-allocate data structures ~Interrupt(); // de-allocate data structures
IntStatus SetLevel(IntStatus level);
// Disable or enable interrupts
// and return previous setting.
void Enable() { (void) SetLevel(IntOn); } IntStatus SetLevel(IntStatus level);
// Enable interrupts. // Disable or enable interrupts
IntStatus getLevel() {return level;} // and return previous setting.
// 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 Enable() { (void)SetLevel(IntOn); }
// Enable interrupts.
IntStatus getLevel() { return level; }
// Return whether interrupts
// are enabled or disabled
void PrintInt(int number); void Idle(); // The ready queue is empty, roll
int CreateFile(char *filename); // simulated time forward until the
// next interrupt
void YieldOnReturn(); // cause a context switch on return
// from an interrupt handler
MachineStatus getStatus() { return status; } void Halt(); // quit and print out stats
void setStatus(MachineStatus st) { status = st; }
// idle, kernel, user
void DumpState(); // Print interrupt state void PrintInt(int number);
int CreateFile(char* filename);
OpenFileId OpenFile(char* filename);
int WriteFile(char* buffer, int size, OpenFileId fd);
int CloseFile(OpenFileId fd);
int ReadFile(char* buffer, int size, OpenFileId fd);
// NOTE: the following are internal to the hardware simulation code. void YieldOnReturn(); // cause a context switch on return
// DO NOT call these directly. I should make them "private", // from an interrupt handler
// but they need to be public since they are called by the
// hardware device simulators.
void Schedule(CallBackObj *callTo, int when, IntType type); MachineStatus getStatus() { return status; }
// Schedule an interrupt to occur void setStatus(MachineStatus st) { status = st; }
// at time "when". This is called // idle, kernel, user
// by the hardware device simulators.
void OneTick(); // Advance simulated time
private: void DumpState(); // Print interrupt state
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); // NOTE: the following are internal to the hardware simulation code.
// Check if any interrupts are supposed // DO NOT call these directly. I should make them "private",
// to occur now, and if so, do them // but they need to be public since they are called by the
// hardware device simulators.
void ChangeLevel(IntStatus old, // SetLevel, without advancing the void Schedule(CallBackObj* callTo, int when, IntType type);
IntStatus now); // simulated time // 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 #endif // INTERRRUPT_H

View File

@@ -52,7 +52,9 @@ enum ExceptionType { NoException, // Everything ok!
// address space // address space
OverflowException, // Integer overflow in add or sub. OverflowException, // Integer overflow in add or sub.
IllegalInstrException, // Unimplemented or reserved instr. IllegalInstrException, // Unimplemented or reserved instr.
MemoryLimitException, // Insufficient memory
NumExceptionTypes NumExceptionTypes
}; };

View File

@@ -20,25 +20,25 @@
// The fields in this class are public to make it easier to update. // The fields in this class are public to make it easier to update.
class Statistics { class Statistics {
public: public:
int totalTicks; // Total time running Nachos int totalTicks; // Total time running Nachos
int idleTicks; // Time spent idle (no threads to run) int idleTicks; // Time spent idle (no threads to run)
int systemTicks; // Time spent executing system code int systemTicks; // Time spent executing system code
int userTicks; // Time spent executing user code int userTicks; // Time spent executing user code
// (this is also equal to # of // (this is also equal to # of
// user instructions executed) // user instructions executed)
int numDiskReads; // number of disk read requests int numDiskReads; // number of disk read requests
int numDiskWrites; // number of disk write requests int numDiskWrites; // number of disk write requests
int numConsoleCharsRead; // number of characters read from the keyboard int numConsoleCharsRead; // number of characters read from the keyboard
int numConsoleCharsWritten; // number of characters written to the display int numConsoleCharsWritten; // number of characters written to the display
int numPageFaults; // number of virtual memory page faults int numPageFaults; // number of virtual memory page faults
int numPacketsSent; // number of packets sent over the network int numPacketsSent; // number of packets sent over the network
int numPacketsRecvd; // number of packets received over the network int numPacketsRecvd; // number of packets received over the network
Statistics(); // initialize everything to zero Statistics(); // initialize everything to zero
void Print(); // print collected statistics void Print(); // print collected statistics
}; };
// Constants used to reflect the relative time an operation would // Constants used to reflect the relative time an operation would
@@ -49,12 +49,15 @@ class Statistics {
// in the kernel measured by the number of calls to enable interrupts, // in the kernel measured by the number of calls to enable interrupts,
// these time constants are none too exact. // these time constants are none too exact.
const int UserTick = 1; // advance for each user-level instruction const int UserTick = 1; // advance for each user-level instruction
const int SystemTick = 10; // advance each time interrupts are enabled const int SystemTick = 10; // advance each time interrupts are enabled
const int RotationTime = 500; // time disk takes to rotate one sector 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 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 // MP4 MODIFIED
const int TimerTicks = 100; // (average) time between timer interrupts const int ConsoleTime = 1; // 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 #endif // STATS_H

View File

@@ -107,13 +107,13 @@ LD = $(GCCDIR)ld
INCDIR =-I../userprog -I../lib INCDIR =-I../userprog -I../lib
CFLAGS = -G 0 -c $(INCDIR) -B../../usr/local/nachos/lib/gcc-lib/decstation-ultrix/2.95.2/ -B../../usr/local/nachos/decstation-ultrix/bin/ CFLAGS = -G 0 -c $(INCDIR) -B../../usr/local/nachos/lib/gcc-lib/decstation-ultrix/2.95.2/ -B../../usr/local/nachos/decstation-ultrix/bin/
NACHOS = ../build.linux/nachos
ifeq ($(hosttype),unknown) ifeq ($(hosttype),unknown)
PROGRAMS = unknownhost PROGRAMS = unknownhost
else else
# change this if you create a new test program! # change this if you create a new test program!
# PROGRAMS = add halt consoleIO_test1 consoleIO_test2 fileIO_test1 fileIO_test2 PROGRAMS = mp4_consoleIO_1 mp4_consoleIO_2 mp4_consoleIO_3 mp4_consoleIO_4
PROGRAMS = halt
endif endif
all: $(PROGRAMS) all: $(PROGRAMS)
@@ -121,73 +121,35 @@ all: $(PROGRAMS)
start.o: start.S ../userprog/syscall.h start.o: start.S ../userprog/syscall.h
$(CC) $(CFLAGS) $(ASFLAGS) -c start.S $(CC) $(CFLAGS) $(ASFLAGS) -c start.S
halt.o: halt.c mp4_consoleIO_1.o: mp4_consoleIO_1.c
$(CC) $(CFLAGS) -c halt.c $(CC) $(CFLAGS) -c mp4_consoleIO_1.c
halt: halt.o start.o
$(LD) $(LDFLAGS) start.o halt.o -o halt.coff
$(COFF2NOFF) halt.coff halt
add.o: add.c mp4_consoleIO_1: mp4_consoleIO_1.o start.o
$(CC) $(CFLAGS) -c add.c $(LD) $(LDFLAGS) start.o mp4_consoleIO_1.o -o mp4_consoleIO_1.coff
$(COFF2NOFF) mp4_consoleIO_1.coff mp4_consoleIO_1
add: add.o start.o
$(LD) $(LDFLAGS) start.o add.o -o add.coff
$(COFF2NOFF) add.coff add
shell.o: shell.c mp4_consoleIO_2.o: mp4_consoleIO_2.c
$(CC) $(CFLAGS) -c shell.c $(CC) $(CFLAGS) -c mp4_consoleIO_2.c
shell: shell.o start.o
$(LD) $(LDFLAGS) start.o shell.o -o shell.coff
$(COFF2NOFF) shell.coff shell
sort.o: sort.c mp4_consoleIO_2: mp4_consoleIO_2.o start.o
$(CC) $(CFLAGS) -c sort.c $(LD) $(LDFLAGS) start.o mp4_consoleIO_2.o -o mp4_consoleIO_2.coff
sort: sort.o start.o $(COFF2NOFF) mp4_consoleIO_2.coff mp4_consoleIO_2
$(LD) $(LDFLAGS) start.o sort.o -o sort.coff
$(COFF2NOFF) sort.coff sort
segments.o: segments.c mp4_consoleIO_3.o: mp4_consoleIO_3.c
$(CC) $(CFLAGS) -c segments.c $(CC) $(CFLAGS) -c mp4_consoleIO_3.c
segments: segments.o start.o
$(LD) $(LDFLAGS) start.o segments.o -o segments.coff
$(COFF2NOFF) segments.coff segments
matmult.o: matmult.c mp4_consoleIO_3: mp4_consoleIO_3.o start.o
$(CC) $(CFLAGS) -c matmult.c $(LD) $(LDFLAGS) start.o mp4_consoleIO_3.o -o mp4_consoleIO_3.coff
matmult: matmult.o start.o $(COFF2NOFF) mp4_consoleIO_3.coff mp4_consoleIO_3
$(LD) $(LDFLAGS) start.o matmult.o -o matmult.coff
$(COFF2NOFF) matmult.coff matmult
consoleIO_test1.o: consoleIO_test1.c mp4_consoleIO_4.o: mp4_consoleIO_4.c
$(CC) $(CFLAGS) -c consoleIO_test1.c $(CC) $(CFLAGS) -c mp4_consoleIO_4.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 mp4_consoleIO_4: mp4_consoleIO_4.o start.o
$(CC) $(CFLAGS) -c consoleIO_test2.c $(LD) $(LDFLAGS) start.o mp4_consoleIO_4.o -o mp4_consoleIO_4.coff
consoleIO_test2: consoleIO_test2.o start.o $(COFF2NOFF) mp4_consoleIO_4.coff mp4_consoleIO_4
$(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
fileIO_test3.o: fileIO_test3.c
$(CC) $(CFLAGS) -c fileIO_test3.c
fileIO_test3: fileIO_test3.o start.o
$(LD) $(LDFLAGS) start.o fileIO_test3.o -o fileIO_test3.coff
$(COFF2NOFF) fileIO_test3.coff fileIO_test3
clean: clean:
$(RM) -f *.o *.ii $(RM) -f *.o *.ii
@@ -196,6 +158,13 @@ clean:
distclean: clean distclean: clean
$(RM) -f $(PROGRAMS) $(RM) -f $(PROGRAMS)
run: $(PROGRAMS)
timeout 1 $(NACHOS) -ep mp4_consoleIO_1 70 -ep mp4_consoleIO_3 80 -ep mp4_consoleIO_2 50
echo 'done'
debug: $(PROGRAMS)
timeout 1 $(NACHOS) -d z -ep mp4_consoleIO_1 60 -ep mp4_consoleIO_2 70
unknownhost: unknownhost:
@echo Host type could not be determined. @echo Host type could not be determined.
@echo make is terminating. @echo make is terminating.

Binary file not shown.

Binary file not shown.

View File

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

Binary file not shown.

View File

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

1
code/test/file1.test Normal file
View File

@@ -0,0 +1 @@
abcdefghijklmnopqrstuvwxyz

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,10 @@
#include "syscall.h"
int main()
{
int n;
for (n = 0; n < 4; n++) {
PrintInt(1);
}
return 0;
}

View File

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

View File

@@ -0,0 +1,10 @@
#include "syscall.h"
int
main()
{
int n;
for (n = 0; n < 12; n++) {
PrintInt(3);
}
return 0;
}

View File

@@ -0,0 +1,9 @@
#include "syscall.h"
int main()
{
int n;
for (n = 0; n < 11; n++) {
PrintInt(4);
}
return 0;
}

View File

@@ -1,3 +1,9 @@
make clean make distclean
make -d make
../build.linux/nachos -e halt rm -f *.o *.ii
rm -f *.coff
echo "=========================="
timeout 1 ../build.linux/nachos -e mp4_consoleIO_1 -e mp4_consoleIO_2
echo "=========================="
timeout 1 ../build.linux/nachos -e mp4_consoleIO_3 -e mp4_consoleIO_4
echo "done"

View File

@@ -58,6 +58,14 @@ MSG:
j $31 j $31
.end MSG .end MSG
.globl PrintInt
.ent PrintInt
PrintInt:
addiu $2,$0,SC_PrintInt
syscall
j $31
.end PrintInt
.globl Add .globl Add
.ent Add .ent Add
Add: Add:

6
code/test/test.c Normal file
View File

@@ -0,0 +1,6 @@
#include "syscall.h"
int main()
{
Exit(0);
}

View File

@@ -43,13 +43,15 @@ Alarm::Alarm(bool doRandom)
// if we're currently running something (in other words, not idle). // if we're currently running something (in other words, not idle).
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void
Alarm::CallBack() Alarm::CallBack()
{ {
Interrupt *interrupt = kernel->interrupt; Interrupt* interrupt = kernel->interrupt;
MachineStatus status = interrupt->getStatus(); MachineStatus status = interrupt->getStatus();
// Todo ----
if (status != IdleMode) { if (status != IdleMode) {
interrupt->YieldOnReturn(); // interrupt->YieldOnReturn();
} }
// ---------
} }

View File

@@ -24,60 +24,76 @@
// for the initialization (see also comments in main.cc) // for the initialization (see also comments in main.cc)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
Kernel::Kernel(int argc, char **argv) Kernel::Kernel(int argc, char** argv)
{ {
randomSlice = FALSE; randomSlice = FALSE;
debugUserProg = FALSE; debugUserProg = FALSE;
consoleIn = NULL; // default is stdin consoleIn = NULL; // default is stdin
consoleOut = NULL; // default is stdout consoleOut = NULL; // default is stdout
#ifndef FILESYS_STUB #ifndef FILESYS_STUB
formatFlag = FALSE; formatFlag = FALSE;
#endif #endif
reliability = 1; // network reliability, default is 1.0 reliability = 1; // network reliability, default is 1.0
hostName = 0; // machine id, also UNIX socket name hostName = 0; // machine id, also UNIX socket name
// 0 is the default machine id // 0 is the default machine id
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-rs") == 0) { if (strcmp(argv[i], "-rs") == 0) {
ASSERT(i + 1 < argc); ASSERT(i + 1 < argc);
RandomInit(atoi(argv[i + 1]));// initialize pseudo-random RandomInit(atoi(argv[i + 1]));// initialize pseudo-random
// number generator // number generator
randomSlice = TRUE; randomSlice = TRUE;
i++; i++;
} else if (strcmp(argv[i], "-s") == 0) {
debugUserProg = TRUE;
} else if (strcmp(argv[i], "-e") == 0) {
execfile[++execfileNum]= argv[++i];
cout << execfile[execfileNum] << "\n";
} 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";
}
} }
else if (strcmp(argv[i], "-s") == 0) {
debugUserProg = TRUE;
}
// Todo ----
else if (strcmp(argv[i], "-e") == 0) {
execfile[++execfileNum] = argv[++i];
cout << execfile[execfileNum] << "\n";
}
else if (strcmp(argv[i], "-ep") == 0) {
execfile[++execfileNum] = argv[++i];
execPriority[execfileNum] = atoi(argv[++i]);
cout << execfile[execfileNum] << " with priority "
<< execPriority[execfileNum] << "\n";
}
// ---------
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";
}
}
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -94,8 +110,8 @@ Kernel::Initialize()
// But if it ever tries to give up the CPU, we better have a Thread // But if it ever tries to give up the CPU, we better have a Thread
// object to save its state. // object to save its state.
currentThread = new Thread("main", threadNum++); currentThread = new Thread("main", threadNum++);
currentThread->setStatus(RUNNING); currentThread->setStatus(RUNNING);
stats = new Statistics(); // collect statistics stats = new Statistics(); // collect statistics
@@ -111,9 +127,12 @@ Kernel::Initialize()
#else #else
fileSystem = new FileSystem(formatFlag); fileSystem = new FileSystem(formatFlag);
#endif // FILESYS_STUB #endif // FILESYS_STUB
postOfficeIn = new PostOfficeInput(10);
postOfficeOut = new PostOfficeOutput(reliability);
// MP4 MODIFIED
// postOfficeIn = new PostOfficeInput(10);
// postOfficeOut = new PostOfficeOutput(reliability);
frameTable = new FrameTable();
interrupt->Enable(); interrupt->Enable();
} }
@@ -133,9 +152,13 @@ Kernel::~Kernel()
delete synchConsoleOut; delete synchConsoleOut;
delete synchDisk; delete synchDisk;
delete fileSystem; delete fileSystem;
delete postOfficeIn;
delete postOfficeOut; // MP4 MODIFIED
// delete postOfficeIn;
// delete postOfficeOut;
delete frameTable;
Exit(0); Exit(0);
} }
@@ -146,23 +169,23 @@ Kernel::~Kernel()
void void
Kernel::ThreadSelfTest() { Kernel::ThreadSelfTest() {
Semaphore *semaphore; Semaphore* semaphore;
SynchList<int> *synchList; SynchList<int>* synchList;
LibSelfTest(); // test library routines LibSelfTest(); // test library routines
currentThread->SelfTest(); // test thread switching currentThread->SelfTest(); // test thread switching
// test semaphore operation // test semaphore operation
semaphore = new Semaphore("test", 0); semaphore = new Semaphore("test", 0);
semaphore->SelfTest(); semaphore->SelfTest();
delete semaphore; delete semaphore;
// test locks, condition variables // test locks, condition variables
// using synchronized lists // using synchronized lists
synchList = new SynchList<int>; synchList = new SynchList<int>;
synchList->SelfTest(9); synchList->SelfTest(9);
delete synchList; delete synchList;
} }
@@ -175,14 +198,14 @@ void
Kernel::ConsoleTest() { Kernel::ConsoleTest() {
char ch; char ch;
cout << "Testing the console device.\n" cout << "Testing the console device.\n"
<< "Typed characters will be echoed, until ^D is typed.\n" << "Typed characters will be echoed, until ^D is typed.\n"
<< "Note newlines are needed to flush input through UNIX.\n"; << "Note newlines are needed to flush input through UNIX.\n";
cout.flush(); cout.flush();
do { do {
ch = synchConsoleIn->GetChar(); ch = synchConsoleIn->GetChar();
if(ch != EOF) synchConsoleOut->PutChar(ch); // echo it! if (ch != EOF) synchConsoleOut->PutChar(ch); // echo it!
} while (ch != EOF); } while (ch != EOF);
cout << "\n"; cout << "\n";
@@ -207,28 +230,28 @@ Kernel::NetworkTest() {
if (hostName == 0 || hostName == 1) { if (hostName == 0 || hostName == 1) {
// if we're machine 1, send to 0 and vice versa // if we're machine 1, send to 0 and vice versa
int farHost = (hostName == 0 ? 1 : 0); int farHost = (hostName == 0 ? 1 : 0);
PacketHeader outPktHdr, inPktHdr; PacketHeader outPktHdr, inPktHdr;
MailHeader outMailHdr, inMailHdr; MailHeader outMailHdr, inMailHdr;
char *data = "Hello there!"; char* data = "Hello there!";
char *ack = "Got it!"; char* ack = "Got it!";
char buffer[MaxMailSize]; char buffer[MaxMailSize];
// construct packet, mail header for original message // construct packet, mail header for original message
// To: destination machine, mailbox 0 // To: destination machine, mailbox 0
// From: our machine, reply to: mailbox 1 // From: our machine, reply to: mailbox 1
outPktHdr.to = farHost; outPktHdr.to = farHost;
outMailHdr.to = 0; outMailHdr.to = 0;
outMailHdr.from = 1; outMailHdr.from = 1;
outMailHdr.length = strlen(data) + 1; outMailHdr.length = strlen(data) + 1;
// Send the first message // Send the first message
postOfficeOut->Send(outPktHdr, outMailHdr, data); postOfficeOut->Send(outPktHdr, outMailHdr, data);
// Wait for the first message from the other machine // Wait for the first message from the other machine
postOfficeIn->Receive(0, &inPktHdr, &inMailHdr, buffer); postOfficeIn->Receive(0, &inPktHdr, &inMailHdr, buffer);
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box " cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
<< inMailHdr.from << "\n"; << inMailHdr.from << "\n";
cout.flush(); cout.flush();
// Send acknowledgement to the other machine (using "reply to" mailbox // Send acknowledgement to the other machine (using "reply to" mailbox
@@ -236,76 +259,78 @@ Kernel::NetworkTest() {
outPktHdr.to = inPktHdr.from; outPktHdr.to = inPktHdr.from;
outMailHdr.to = inMailHdr.from; outMailHdr.to = inMailHdr.from;
outMailHdr.length = strlen(ack) + 1; outMailHdr.length = strlen(ack) + 1;
postOfficeOut->Send(outPktHdr, outMailHdr, ack); postOfficeOut->Send(outPktHdr, outMailHdr, ack);
// Wait for the ack from the other machine to the first message we sent // Wait for the ack from the other machine to the first message we sent
postOfficeIn->Receive(1, &inPktHdr, &inMailHdr, buffer); postOfficeIn->Receive(1, &inPktHdr, &inMailHdr, buffer);
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box " cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
<< inMailHdr.from << "\n"; << inMailHdr.from << "\n";
cout.flush(); cout.flush();
} }
// Then we're done! // Then we're done!
} }
void ForkExecute(Thread *t) void ForkExecute(Thread* t)
{ {
if ( !t->space->Load(t->getName()) ) { if (!t->space->Load(t->getName())) {
return; // executable not found return; // executable not found
} }
t->space->Execute(t->getName()); t->space->Execute(t->getName());
} }
void Kernel::ExecAll() void Kernel::ExecAll()
{ {
for (int i=1;i<=execfileNum;i++) { for (int i = 1;i <= execfileNum;i++) {
int a = Exec(execfile[i]); int a = Exec(execfile[i], execPriority[i]);
} }
currentThread->Finish(); currentThread->Finish();
//Kernel::Exec(); //Kernel::Exec();
} }
// Todo ----
int Kernel::Exec(char* name) int Kernel::Exec(char* name, int priority)
{ {
t[threadNum] = new Thread(name, threadNum); t[threadNum] = new Thread(name, threadNum);
t[threadNum]->space = new AddrSpace(); t[threadNum]->setPriority(priority);
t[threadNum]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[threadNum]); // ---------
threadNum++; t[threadNum]->space = new AddrSpace();
t[threadNum]->Fork((VoidFunctionPtr)&ForkExecute, (void*)t[threadNum]);
threadNum++;
return threadNum-1; return threadNum - 1;
/* /*
cout << "Total threads number is " << execfileNum << endl; cout << "Total threads number is " << execfileNum << endl;
for (int n=1;n<=execfileNum;n++) { for (int n=1;n<=execfileNum;n++) {
t[n] = new Thread(execfile[n]); t[n] = new Thread(execfile[n]);
t[n]->space = new AddrSpace(); t[n]->space = new AddrSpace();
t[n]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[n]); t[n]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[n]);
cout << "Thread " << execfile[n] << " is executing." << endl; cout << "Thread " << execfile[n] << " is executing." << endl;
} }
cout << "debug Kernel::Run finished.\n"; cout << "debug Kernel::Run finished.\n";
*/ */
// Thread *t1 = new Thread(execfile[1]); // Thread *t1 = new Thread(execfile[1]);
// Thread *t1 = new Thread("../test/test1"); // Thread *t1 = new Thread("../test/test1");
// Thread *t2 = new Thread("../test/test2"); // Thread *t2 = new Thread("../test/test2");
// AddrSpace *halt = new AddrSpace(); // AddrSpace *halt = new AddrSpace();
// t1->space = new AddrSpace(); // t1->space = new AddrSpace();
// t2->space = new AddrSpace(); // t2->space = new AddrSpace();
// halt->Execute("../test/halt"); // halt->Execute("../test/halt");
// t1->Fork((VoidFunctionPtr) &ForkExecute, (void *)t1); // t1->Fork((VoidFunctionPtr) &ForkExecute, (void *)t1);
// t2->Fork((VoidFunctionPtr) &ForkExecute, (void *)t2); // t2->Fork((VoidFunctionPtr) &ForkExecute, (void *)t2);
// currentThread->Finish(); // currentThread->Finish();
// Kernel::Run(); // Kernel::Run();
// cout << "after ThreadedKernel:Run();" << endl; // unreachable // cout << "after ThreadedKernel:Run();" << endl; // unreachable
} }
int Kernel::CreateFile(char *filename) int Kernel::CreateFile(char* filename)
{ {
return fileSystem->Create(filename); return fileSystem->Create(filename);
} }

View File

@@ -25,29 +25,34 @@ class SynchConsoleInput;
class SynchConsoleOutput; class SynchConsoleOutput;
class SynchDisk; class SynchDisk;
// Todo ----
// ---------
class Kernel { class Kernel {
public: public:
Kernel(int argc, char **argv); Kernel(int argc, char **argv);
// Interpret command line arguments // Interpret command line arguments
~Kernel(); // deallocate the kernel ~Kernel(); // deallocate the kernel
void Initialize(); // initialize the kernel -- separated void Initialize(); // initialize the kernel -- separated
// from constructor because // from constructor because
// refers to "kernel" as a global // refers to "kernel" as a global
void ExecAll(); void ExecAll();
int Exec(char* name);
// Todo ----
int Exec(char* name, int priority);
// ---------
void ThreadSelfTest(); // self test of threads and synchronization void ThreadSelfTest(); // self test of threads and synchronization
void ConsoleTest(); // interactive console self test void ConsoleTest(); // interactive console self test
void NetworkTest(); // interactive 2-machine network test void NetworkTest(); // interactive 2-machine network test
Thread* getThread(int threadID){return t[threadID];} Thread* getThread(int threadID){return t[threadID];}
int CreateFile(char* filename); // fileSystem call
// These are public for notational convenience; really, int CreateFile(char* filename); // fileSystem call
// they're global variables used everywhere.
// These are public for notational convenience; really,
// they're global variables used everywhere.
Thread *currentThread; // the thread holding the CPU Thread *currentThread; // the thread holding the CPU
Scheduler *scheduler; // the ready list Scheduler *scheduler; // the ready list
@@ -61,15 +66,20 @@ class Kernel {
FileSystem *fileSystem; FileSystem *fileSystem;
PostOfficeInput *postOfficeIn; PostOfficeInput *postOfficeIn;
PostOfficeOutput *postOfficeOut; PostOfficeOutput *postOfficeOut;
FrameTable *frameTable;
int hostName; // machine identifier int hostName; // machine identifier
private: private:
Thread* t[10]; Thread* t[10];
char* execfile[10];
int execfileNum; // Todo ----
int threadNum; char* execfile[10];
int execPriority[10];
// ---------
int execfileNum;
int threadNum;
bool randomSlice; // enable pseudo-random time slicing bool randomSlice; // enable pseudo-random time slicing
bool debugUserProg; // single step user program bool debugUserProg; // single step user program
double reliability; // likelihood messages are dropped double reliability; // likelihood messages are dropped
@@ -82,5 +92,3 @@ class Kernel {
#endif // KERNEL_H #endif // KERNEL_H

View File

@@ -47,8 +47,8 @@
#include "sysdep.h" #include "sysdep.h"
// global variables // global variables
Kernel *kernel; Kernel* kernel;
Debug *debug; Debug* debug;
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -56,11 +56,11 @@ Debug *debug;
// Delete kernel data structures; called when user hits "ctl-C". // Delete kernel data structures; called when user hits "ctl-C".
//---------------------------------------------------------------------- //----------------------------------------------------------------------
static void static void
Cleanup(int x) Cleanup(int x)
{ {
cerr << "\nCleaning up after signal " << x << "\n"; cerr << "\nCleaning up after signal " << x << "\n";
delete kernel; delete kernel;
} }
//------------------------------------------------------------------- //-------------------------------------------------------------------
@@ -78,42 +78,42 @@ static const int TransferSize = 128;
//---------------------------------------------------------------------- //----------------------------------------------------------------------
static void static void
Copy(char *from, char *to) Copy(char* from, char* to)
{ {
int fd; int fd;
OpenFile* openFile; OpenFile* openFile;
int amountRead, fileLength; int amountRead, fileLength;
char *buffer; char* buffer;
// Open UNIX file // Open UNIX file
if ((fd = OpenForReadWrite(from,FALSE)) < 0) { if ((fd = OpenForReadWrite(from, FALSE)) < 0) {
printf("Copy: couldn't open input file %s\n", from); printf("Copy: couldn't open input file %s\n", from);
return; return;
} }
// Figure out length of UNIX file // Figure out length of UNIX file
Lseek(fd, 0, 2); Lseek(fd, 0, 2);
fileLength = Tell(fd); fileLength = Tell(fd);
Lseek(fd, 0, 0); Lseek(fd, 0, 0);
// Create a Nachos file of the same length // Create a Nachos file of the same length
DEBUG('f', "Copying file " << from << " of size " << fileLength << " to file " << to); DEBUG('f', "Copying file " << from << " of size " << fileLength << " to file " << to);
if (!kernel->fileSystem->Create(to, fileLength)) { // Create Nachos file if (!kernel->fileSystem->Create(to, fileLength)) { // Create Nachos file
printf("Copy: couldn't create output file %s\n", to); printf("Copy: couldn't create output file %s\n", to);
Close(fd); Close(fd);
return; return;
} }
openFile = kernel->fileSystem->Open(to); openFile = kernel->fileSystem->Open(to);
ASSERT(openFile != NULL); 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 // 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; delete openFile;
Close(fd); Close(fd);
} }
@@ -126,22 +126,22 @@ Copy(char *from, char *to)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void
Print(char *name) Print(char* name)
{ {
OpenFile *openFile; OpenFile* openFile;
int i, amountRead; int i, amountRead;
char *buffer; char* buffer;
if ((openFile = kernel->fileSystem->Open(name)) == NULL) { if ((openFile = kernel->fileSystem->Open(name)) == NULL) {
printf("Print: unable to open file %s\n", name); printf("Print: unable to open file %s\n", name);
return; return;
} }
buffer = new char[TransferSize]; buffer = new char[TransferSize];
while ((amountRead = openFile->Read(buffer, TransferSize)) > 0) while ((amountRead = openFile->Read(buffer, TransferSize)) > 0)
for (i = 0; i < amountRead; i++) for (i = 0; i < amountRead; i++)
printf("%c", buffer[i]); printf("%c", buffer[i]);
delete [] buffer; delete[] buffer;
delete openFile; // close the Nachos file delete openFile; // close the Nachos file
return; return;
@@ -164,19 +164,19 @@ Print(char *name)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
int int
main(int argc, char **argv) main(int argc, char** argv)
{ {
int i; int i;
char *debugArg = ""; char* debugArg = "";
char *userProgName = NULL; // default is not to execute a user prog char* userProgName = NULL; // default is not to execute a user prog
bool threadTestFlag = false; bool threadTestFlag = false;
bool consoleTestFlag = false; bool consoleTestFlag = false;
bool networkTestFlag = false; bool networkTestFlag = false;
#ifndef FILESYS_STUB #ifndef FILESYS_STUB
char *copyUnixFileName = NULL; // UNIX file to be copied into Nachos char* copyUnixFileName = NULL; // UNIX file to be copied into Nachos
char *copyNachosFileName = NULL; // name of copied file in Nachos char* copyNachosFileName = NULL; // name of copied file in Nachos
char *printFileName = NULL; char* printFileName = NULL;
char *removeFileName = NULL; char* removeFileName = NULL;
bool dirListFlag = false; bool dirListFlag = false;
bool dumpFlag = false; bool dumpFlag = false;
#endif //FILESYS_STUB #endif //FILESYS_STUB
@@ -186,65 +186,65 @@ main(int argc, char **argv)
// the Kernel constructor // the Kernel constructor
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0) { if (strcmp(argv[i], "-d") == 0) {
ASSERT(i + 1 < argc); // next argument is debug string ASSERT(i + 1 < argc); // next argument is debug string
debugArg = argv[i + 1]; debugArg = argv[i + 1];
i++; i++;
} }
else if (strcmp(argv[i], "-z") == 0) { else if (strcmp(argv[i], "-z") == 0) {
cout << copyright << "\n"; cout << copyright << "\n";
} }
else if (strcmp(argv[i], "-x") == 0) { else if (strcmp(argv[i], "-x") == 0) {
ASSERT(i + 1 < argc); ASSERT(i + 1 < argc);
userProgName = argv[i + 1]; userProgName = argv[i + 1];
i++; i++;
} }
else if (strcmp(argv[i], "-K") == 0) { else if (strcmp(argv[i], "-K") == 0) {
threadTestFlag = TRUE; threadTestFlag = TRUE;
} }
else if (strcmp(argv[i], "-C") == 0) { else if (strcmp(argv[i], "-C") == 0) {
consoleTestFlag = TRUE; consoleTestFlag = TRUE;
} }
else if (strcmp(argv[i], "-N") == 0) { else if (strcmp(argv[i], "-N") == 0) {
networkTestFlag = TRUE; networkTestFlag = TRUE;
} }
#ifndef FILESYS_STUB #ifndef FILESYS_STUB
else if (strcmp(argv[i], "-cp") == 0) { else if (strcmp(argv[i], "-cp") == 0) {
ASSERT(i + 2 < argc); ASSERT(i + 2 < argc);
copyUnixFileName = argv[i + 1]; copyUnixFileName = argv[i + 1];
copyNachosFileName = argv[i + 2]; copyNachosFileName = argv[i + 2];
i += 2; i += 2;
} }
else if (strcmp(argv[i], "-p") == 0) { else if (strcmp(argv[i], "-p") == 0) {
ASSERT(i + 1 < argc); ASSERT(i + 1 < argc);
printFileName = argv[i + 1]; printFileName = argv[i + 1];
i++; i++;
} }
else if (strcmp(argv[i], "-r") == 0) { else if (strcmp(argv[i], "-r") == 0) {
ASSERT(i + 1 < argc); ASSERT(i + 1 < argc);
removeFileName = argv[i + 1]; removeFileName = argv[i + 1];
i++; i++;
} }
else if (strcmp(argv[i], "-l") == 0) { else if (strcmp(argv[i], "-l") == 0) {
dirListFlag = true; dirListFlag = true;
} }
else if (strcmp(argv[i], "-D") == 0) { else if (strcmp(argv[i], "-D") == 0) {
dumpFlag = true; dumpFlag = true;
} }
#endif //FILESYS_STUB #endif //FILESYS_STUB
else if (strcmp(argv[i], "-u") == 0) { else if (strcmp(argv[i], "-u") == 0) {
cout << "Partial usage: nachos [-z -d debugFlags]\n"; cout << "Partial usage: nachos [-z -d debugFlags]\n";
cout << "Partial usage: nachos [-x programName]\n"; cout << "Partial usage: nachos [-x programName]\n";
cout << "Partial usage: nachos [-K] [-C] [-N]\n"; cout << "Partial usage: nachos [-K] [-C] [-N]\n";
#ifndef FILESYS_STUB #ifndef FILESYS_STUB
cout << "Partial usage: nachos [-cp UnixFile NachosFile]\n"; cout << "Partial usage: nachos [-cp UnixFile NachosFile]\n";
cout << "Partial usage: nachos [-p fileName] [-r fileName]\n"; cout << "Partial usage: nachos [-p fileName] [-r fileName]\n";
cout << "Partial usage: nachos [-l] [-D]\n"; cout << "Partial usage: nachos [-l] [-D]\n";
#endif //FILESYS_STUB #endif //FILESYS_STUB
} }
} }
debug = new Debug(debugArg); debug = new Debug(debugArg);
DEBUG(dbgThread, "Entering main"); DEBUG(dbgThread, "Entering main");
kernel = new Kernel(argc, argv); kernel = new Kernel(argc, argv);
@@ -256,42 +256,42 @@ main(int argc, char **argv)
// at this point, the kernel is ready to do something // at this point, the kernel is ready to do something
// run some tests, if requested // run some tests, if requested
if (threadTestFlag) { if (threadTestFlag) {
kernel->ThreadSelfTest(); // test threads and synchronization kernel->ThreadSelfTest(); // test threads and synchronization
} }
if (consoleTestFlag) { if (consoleTestFlag) {
kernel->ConsoleTest(); // interactive test of the synchronized console kernel->ConsoleTest(); // interactive test of the synchronized console
} }
if (networkTestFlag) { if (networkTestFlag) {
kernel->NetworkTest(); // two-machine test of the network kernel->NetworkTest(); // two-machine test of the network
} }
#ifndef FILESYS_STUB #ifndef FILESYS_STUB
if (removeFileName != NULL) { if (removeFileName != NULL) {
kernel->fileSystem->Remove(removeFileName); kernel->fileSystem->Remove(removeFileName);
} }
if (copyUnixFileName != NULL && copyNachosFileName != NULL) { if (copyUnixFileName != NULL && copyNachosFileName != NULL) {
Copy(copyUnixFileName,copyNachosFileName); Copy(copyUnixFileName, copyNachosFileName);
} }
if (dumpFlag) { if (dumpFlag) {
kernel->fileSystem->Print(); kernel->fileSystem->Print();
} }
if (dirListFlag) { if (dirListFlag) {
kernel->fileSystem->List(); kernel->fileSystem->List();
} }
if (printFileName != NULL) { if (printFileName != NULL) {
Print(printFileName); Print(printFileName);
} }
#endif // FILESYS_STUB #endif // FILESYS_STUB
// finally, run an initial user program if requested to do so // finally, run an initial user program if requested to do so
kernel->ExecAll(); kernel->ExecAll();
// If we don't run a user program, we may get here. // If we don't run a user program, we may get here.
// Calling "return" would terminate the program. // Calling "return" would terminate the program.
// Instead, call Halt, which will first clean up, then // Instead, call Halt, which will first clean up, then
// terminate. // terminate.
// kernel->interrupt->Halt(); // kernel->interrupt->Halt();
ASSERTNOTREACHED(); ASSERTNOTREACHED();
} }

View File

@@ -22,6 +22,7 @@
#include "debug.h" #include "debug.h"
#include "scheduler.h" #include "scheduler.h"
#include "main.h" #include "main.h"
#include <functional>
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Scheduler::Scheduler // Scheduler::Scheduler
@@ -29,11 +30,22 @@
// Initially, no ready threads. // Initially, no ready threads.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Todo ----
int cmp(Thread *a, Thread *b)
{
int ap = a->getPriority();
int bp = b->getPriority();
return (ap < bp) - (ap > bp);
}
Scheduler::Scheduler() Scheduler::Scheduler()
{ {
readyList = new List<Thread *>; readyList = new SortedList(cmp);
toBeDestroyed = NULL; // ---------
}
toBeDestroyed = NULL;
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Scheduler::~Scheduler // Scheduler::~Scheduler
@@ -41,9 +53,9 @@ Scheduler::Scheduler()
//---------------------------------------------------------------------- //----------------------------------------------------------------------
Scheduler::~Scheduler() Scheduler::~Scheduler()
{ {
delete readyList; delete readyList;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Scheduler::ReadyToRun // Scheduler::ReadyToRun
@@ -54,13 +66,15 @@ Scheduler::~Scheduler()
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void
Scheduler::ReadyToRun (Thread *thread) Scheduler::ReadyToRun(Thread* thread)
{ {
ASSERT(kernel->interrupt->getLevel() == IntOff); ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName()); DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName());
//cout << "Putting thread on ready list: " << thread->getName() << endl ; //cout << "Putting thread on ready list: " << thread->getName() << endl ;
thread->setStatus(READY); thread->setStatus(READY);
readyList->Append(thread);
DEBUG(dbgSche, "[A] Tick [" << kernel->stats->totalTicks << "]: Process [" << thread->getName() << "] is inserted into queue.");
readyList->Insert(thread);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -71,16 +85,18 @@ Scheduler::ReadyToRun (Thread *thread)
// Thread is removed from the ready list. // Thread is removed from the ready list.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
Thread * Thread*
Scheduler::FindNextToRun () Scheduler::FindNextToRun()
{ {
ASSERT(kernel->interrupt->getLevel() == IntOff); ASSERT(kernel->interrupt->getLevel() == IntOff);
if (readyList->IsEmpty()) { if (readyList->IsEmpty()) {
return NULL; return NULL;
} else { }
return readyList->RemoveFront(); else {
} DEBUG(dbgSche, "[B] Tick [" << kernel->stats->totalTicks << "]: Process [" << readyList->Front()->getName() << "] is removed from queue.");
return readyList->RemoveFront();
}
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -101,52 +117,53 @@ Scheduler::FindNextToRun ()
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void
Scheduler::Run (Thread *nextThread, bool finishing) Scheduler::Run(Thread* nextThread, bool finishing)
{ {
Thread *oldThread = kernel->currentThread; Thread* oldThread = kernel->currentThread;
ASSERT(kernel->interrupt->getLevel() == IntOff);
if (finishing) { // mark that we need to delete current thread ASSERT(kernel->interrupt->getLevel() == IntOff);
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 if (finishing) { // mark that we need to delete current thread
nextThread->setStatus(RUNNING); // nextThread is now running ASSERT(toBeDestroyed == NULL);
toBeDestroyed = oldThread;
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); if (oldThread->space != NULL) { // if this thread is a user program,
oldThread->SaveUserState(); // save the user's CPU registers
oldThread->space->SaveState();
}
// we're back, running oldThread oldThread->CheckOverflow(); // check if the old thread
// had an undetected stack overflow
// interrupts are off when we return from switch!
ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Now in thread: " << oldThread->getName()); kernel->currentThread = nextThread; // switch to the next thread
nextThread->setStatus(RUNNING); // nextThread is now running
CheckToBeDestroyed(); // check if thread we were running DEBUG(dbgThread, "Switching from: " << oldThread->getName() << " to: " << nextThread->getName());
// before this one has finished DEBUG(dbgSche, "[C] Tick [" << kernel->stats->totalTicks << "]: Process [" << nextThread->getName() << "] is now selected for execution, thread [" << oldThread->getName() << "] is replaced.");
// and needs to be cleaned up
// This is a machine-dependent assembly language routine defined
if (oldThread->space != NULL) { // if there is an address space // in switch.s. You may have to think
oldThread->RestoreUserState(); // to restore, do it. // a bit to figure out what happens after this, both from the point
oldThread->space->RestoreState(); // 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();
}
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -160,12 +177,12 @@ Scheduler::Run (Thread *nextThread, bool finishing)
void void
Scheduler::CheckToBeDestroyed() Scheduler::CheckToBeDestroyed()
{ {
if (toBeDestroyed != NULL) { if (toBeDestroyed != NULL) {
delete toBeDestroyed; delete toBeDestroyed;
toBeDestroyed = NULL; toBeDestroyed = NULL;
} }
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Scheduler::Print // Scheduler::Print
// Print the scheduler state -- in other words, the contents of // Print the scheduler state -- in other words, the contents of
@@ -174,6 +191,6 @@ Scheduler::CheckToBeDestroyed()
void void
Scheduler::Print() Scheduler::Print()
{ {
cout << "Ready list contents:\n"; cout << "Ready list contents:\n";
readyList->Apply(ThreadPrint); readyList->Apply(ThreadPrint);
} }

View File

@@ -23,22 +23,25 @@ class Scheduler {
~Scheduler(); // De-allocate ready list ~Scheduler(); // De-allocate ready list
void ReadyToRun(Thread* thread); void ReadyToRun(Thread* thread);
// Thread can be dispatched. // Thread can be dispatched.
Thread* FindNextToRun(); // Dequeue first thread on the ready Thread* FindNextToRun(); // Dequeue first thread on the ready
// list, if any, and return thread. // list, if any, and return thread.
void Run(Thread* nextThread, bool finishing); void Run(Thread* nextThread, bool finishing);
// Cause nextThread to start running // Cause nextThread to start running
void CheckToBeDestroyed();// Check if thread that had been void CheckToBeDestroyed();// Check if thread that had been
// running needs to be deleted // running needs to be deleted
void Print(); // Print contents of ready list void Print(); // Print contents of ready list
// SelfTest for scheduler is implemented in class Thread // SelfTest for scheduler is implemented in class Thread
private: private:
List<Thread *> *readyList; // queue of threads that are ready to run, // Todo ----
// but not running SortedList<Thread *> *readyList; // queue of threads that are ready to run,
// but not running
// ---------
Thread *toBeDestroyed; // finishing thread to be destroyed Thread *toBeDestroyed; // finishing thread to be destroyed
// by the next thread that runs // by the next thread that runs
}; };
#endif // SCHEDULER_H #endif // SCHEDULER_H

View File

@@ -48,7 +48,7 @@ Semaphore::Semaphore(char* debugName, int initialValue)
{ {
name = debugName; name = debugName;
value = initialValue; value = initialValue;
queue = new List<Thread *>; queue = new List<Thread*>;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -75,20 +75,20 @@ Semaphore::~Semaphore()
void void
Semaphore::P() Semaphore::P()
{ {
Interrupt *interrupt = kernel->interrupt; Interrupt* interrupt = kernel->interrupt;
Thread *currentThread = kernel->currentThread; Thread* currentThread = kernel->currentThread;
// disable interrupts // disable interrupts
IntStatus oldLevel = interrupt->SetLevel(IntOff); IntStatus oldLevel = interrupt->SetLevel(IntOff);
while (value == 0) { // semaphore not available while (value == 0) { // semaphore not available
queue->Append(currentThread); // so go to sleep queue->Append(currentThread); // so go to sleep
currentThread->Sleep(FALSE); currentThread->Sleep(FALSE);
} }
value--; // semaphore available, consume its value value--; // semaphore available, consume its value
// re-enable interrupts // re-enable interrupts
(void) interrupt->SetLevel(oldLevel); (void)interrupt->SetLevel(oldLevel);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -102,18 +102,18 @@ Semaphore::P()
void void
Semaphore::V() Semaphore::V()
{ {
Interrupt *interrupt = kernel->interrupt; Interrupt* interrupt = kernel->interrupt;
// disable interrupts // disable interrupts
IntStatus oldLevel = interrupt->SetLevel(IntOff); IntStatus oldLevel = interrupt->SetLevel(IntOff);
if (!queue->IsEmpty()) { // make thread ready. if (!queue->IsEmpty()) { // make thread ready.
kernel->scheduler->ReadyToRun(queue->RemoveFront()); kernel->scheduler->ReadyToRun(queue->RemoveFront());
} }
value++; value++;
// re-enable interrupts // re-enable interrupts
(void) interrupt->SetLevel(oldLevel); (void)interrupt->SetLevel(oldLevel);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -122,27 +122,27 @@ Semaphore::V()
// to control two threads ping-ponging back and forth. // to control two threads ping-ponging back and forth.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
static Semaphore *ping; static Semaphore* ping;
static void static void
SelfTestHelper (Semaphore *pong) SelfTestHelper(Semaphore* pong)
{ {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
ping->P(); ping->P();
pong->V(); pong->V();
} }
} }
void void
Semaphore::SelfTest() Semaphore::SelfTest()
{ {
Thread *helper = new Thread("ping", 1); Thread* helper = new Thread("ping", 1);
ASSERT(value == 0); // otherwise test won't work! ASSERT(value == 0); // otherwise test won't work!
ping = new Semaphore("ping", 0); ping = new Semaphore("ping", 0);
helper->Fork((VoidFunctionPtr) SelfTestHelper, this); helper->Fork((VoidFunctionPtr)SelfTestHelper, this);
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
ping->V(); ping->V();
this->P(); this->P();
} }
delete ping; delete ping;
} }
@@ -213,7 +213,7 @@ void Lock::Release()
Condition::Condition(char* debugName) Condition::Condition(char* debugName)
{ {
name = debugName; name = debugName;
waitQueue = new List<Semaphore *>; waitQueue = new List<Semaphore*>;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -241,18 +241,18 @@ Condition::~Condition()
// "conditionLock" -- lock protecting the use of this condition // "conditionLock" -- lock protecting the use of this condition
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void Condition::Wait(Lock* conditionLock) void Condition::Wait(Lock* conditionLock)
{ {
Semaphore *waiter; Semaphore* waiter;
ASSERT(conditionLock->IsHeldByCurrentThread());
waiter = new Semaphore("condition", 0); ASSERT(conditionLock->IsHeldByCurrentThread());
waitQueue->Append(waiter);
conditionLock->Release(); waiter = new Semaphore("condition", 0);
waiter->P(); waitQueue->Append(waiter);
conditionLock->Acquire(); conditionLock->Release();
delete waiter; waiter->P();
conditionLock->Acquire();
delete waiter;
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -272,13 +272,13 @@ void Condition::Wait(Lock* conditionLock)
void Condition::Signal(Lock* conditionLock) void Condition::Signal(Lock* conditionLock)
{ {
Semaphore *waiter; Semaphore* waiter;
ASSERT(conditionLock->IsHeldByCurrentThread()); ASSERT(conditionLock->IsHeldByCurrentThread());
if (!waitQueue->IsEmpty()) { if (!waitQueue->IsEmpty()) {
waiter = waitQueue->RemoveFront(); waiter = waitQueue->RemoveFront();
waiter->V(); waiter->V();
} }
} }
@@ -289,7 +289,7 @@ void Condition::Signal(Lock* conditionLock)
// "conditionLock" -- lock protecting the use of this condition // "conditionLock" -- lock protecting the use of this condition
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void Condition::Broadcast(Lock* conditionLock) void Condition::Broadcast(Lock* conditionLock)
{ {
while (!waitQueue->IsEmpty()) { while (!waitQueue->IsEmpty()) {
Signal(conditionLock); Signal(conditionLock);

View File

@@ -1,4 +1,4 @@
// thread.cc // thread.cc
// Routines to manage threads. These are the main operations: // Routines to manage threads. These are the main operations:
// //
// Fork -- create a thread to run a procedure concurrently // Fork -- create a thread to run a procedure concurrently
@@ -9,11 +9,11 @@
// Finish -- called when the forked procedure finishes, to clean up // Finish -- called when the forked procedure finishes, to clean up
// Yield -- relinquish control over the CPU to another ready thread // Yield -- relinquish control over the CPU to another ready thread
// Sleep -- relinquish control over the CPU, but thread is now blocked. // Sleep -- relinquish control over the CPU, but thread is now blocked.
// In other words, it will not run again, until explicitly // In other words, it will not run again, until explicitly
// put back on the ready queue. // put back on the ready queue.
// //
// Copyright (c) 1992-1996 The Regents of the University of California. // Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved. See copyright.h for copyright notice and limitation // All rights reserved. See copyright.h for copyright notice and limitation
// of liability and disclaimer of warranty provisions. // of liability and disclaimer of warranty provisions.
#include "copyright.h" #include "copyright.h"
@@ -33,17 +33,18 @@ const int STACK_FENCEPOST = 0xdedbeef;
// "threadName" is an arbitrary string, useful for debugging. // "threadName" is an arbitrary string, useful for debugging.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
Thread::Thread(char* threadName, int threadID) Thread::Thread(char *threadName, int threadID)
{ {
ID = threadID; ID = threadID;
name = threadName; name = threadName;
stackTop = NULL; stackTop = NULL;
stack = NULL; stack = NULL;
status = JUST_CREATED; status = JUST_CREATED;
for (int i = 0; i < MachineStateSize; i++) { for (int i = 0; i < MachineStateSize; i++)
machineState[i] = NULL; // not strictly necessary, since {
// new thread ignores contents machineState[i] = NULL; // not strictly necessary, since
// of machine registers // new thread ignores contents
// of machine registers
} }
space = NULL; space = NULL;
} }
@@ -65,12 +66,12 @@ Thread::~Thread()
DEBUG(dbgThread, "Deleting thread: " << name); DEBUG(dbgThread, "Deleting thread: " << name);
ASSERT(this != kernel->currentThread); ASSERT(this != kernel->currentThread);
if (stack != NULL) if (stack != NULL)
DeallocBoundedArray((char *) stack, StackSize * sizeof(int)); DeallocBoundedArray((char *)stack, StackSize * sizeof(int));
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Thread::Fork // Thread::Fork
// Invoke (*func)(arg), allowing caller and callee to execute // Invoke (*func)(arg), allowing caller and callee to execute
// concurrently. // concurrently.
// //
// NOTE: although our definition allows only a single argument // NOTE: although our definition allows only a single argument
@@ -83,26 +84,25 @@ Thread::~Thread()
// 2. Initialize the stack so that a call to SWITCH will // 2. Initialize the stack so that a call to SWITCH will
// cause it to run the procedure // cause it to run the procedure
// 3. Put the thread on the ready queue // 3. Put the thread on the ready queue
// //
// "func" is the procedure to run concurrently. // "func" is the procedure to run concurrently.
// "arg" is a single argument to be passed to the procedure. // "arg" is a single argument to be passed to the procedure.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::Fork(VoidFunctionPtr func, void *arg)
Thread::Fork(VoidFunctionPtr func, void *arg)
{ {
Interrupt *interrupt = kernel->interrupt; Interrupt *interrupt = kernel->interrupt;
Scheduler *scheduler = kernel->scheduler; Scheduler *scheduler = kernel->scheduler;
IntStatus oldLevel; IntStatus oldLevel;
DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int) func << " " << arg); DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int)func << " " << arg);
StackAllocate(func, arg); StackAllocate(func, arg);
oldLevel = interrupt->SetLevel(IntOff); oldLevel = interrupt->SetLevel(IntOff);
scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts
// are disabled! // are disabled!
(void) interrupt->SetLevel(oldLevel); (void)interrupt->SetLevel(oldLevel);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Thread::CheckOverflow // Thread::CheckOverflow
@@ -119,16 +119,16 @@ Thread::Fork(VoidFunctionPtr func, void *arg)
// Don't do this: void foo() { int bigArray[10000]; ... } // Don't do this: void foo() { int bigArray[10000]; ... }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::CheckOverflow()
Thread::CheckOverflow()
{ {
if (stack != NULL) { if (stack != NULL)
#ifdef HPUX // Stacks grow upward on the Snakes {
ASSERT(stack[StackSize - 1] == STACK_FENCEPOST); #ifdef HPUX // Stacks grow upward on the Snakes
ASSERT(stack[StackSize - 1] == STACK_FENCEPOST);
#else #else
ASSERT(*stack == STACK_FENCEPOST); ASSERT(*stack == STACK_FENCEPOST);
#endif #endif
} }
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -137,28 +137,27 @@ Thread::CheckOverflow()
// executing the forked procedure. // executing the forked procedure.
// //
// It's main responsibilities are: // It's main responsibilities are:
// 1. deallocate the previously running thread if it finished // 1. deallocate the previously running thread if it finished
// (see Thread::Finish()) // (see Thread::Finish())
// 2. enable interrupts (so we can get time-sliced) // 2. enable interrupts (so we can get time-sliced)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::Begin()
Thread::Begin ()
{ {
ASSERT(this == kernel->currentThread); ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Beginning thread: " << name); DEBUG(dbgThread, "Beginning thread: " << name);
kernel->scheduler->CheckToBeDestroyed(); kernel->scheduler->CheckToBeDestroyed();
kernel->interrupt->Enable(); kernel->interrupt->Enable();
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Thread::Finish // Thread::Finish
// Called by ThreadRoot when a thread is done executing the // Called by ThreadRoot when a thread is done executing the
// forked procedure. // forked procedure.
// //
// NOTE: we can't immediately de-allocate the thread data structure // NOTE: we can't immediately de-allocate the thread data structure
// or the execution stack, because we're still running in the thread // or the execution stack, because we're still running in the thread
// and we're still on the stack! Instead, we tell the scheduler // 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. // to call the destructor, once it is running in the context of a different thread.
// //
@@ -167,18 +166,16 @@ Thread::Begin ()
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// //
void void Thread::Finish()
Thread::Finish ()
{ {
(void) kernel->interrupt->SetLevel(IntOff); (void)kernel->interrupt->SetLevel(IntOff);
ASSERT(this == kernel->currentThread); ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Finishing thread: " << name); DEBUG(dbgThread, "Finishing thread: " << name);
Sleep(TRUE); // invokes SWITCH Sleep(TRUE); // invokes SWITCH
// not reached // not reached
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Thread::Yield // Thread::Yield
// Relinquish the CPU if any other thread is ready to run. // Relinquish the CPU if any other thread is ready to run.
@@ -192,33 +189,33 @@ Thread::Finish ()
// NOTE: we disable interrupts, so that looking at the thread // NOTE: we disable interrupts, so that looking at the thread
// on the front of the ready list, and switching to it, can be done // 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 // atomically. On return, we re-set the interrupt level to its
// original state, in case we are called with interrupts disabled. // original state, in case we are called with interrupts disabled.
// //
// Similar to Thread::Sleep(), but a little different. // Similar to Thread::Sleep(), but a little different.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::Yield()
Thread::Yield ()
{ {
Thread *nextThread; Thread *nextThread;
IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff); IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff);
ASSERT(this == kernel->currentThread); ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Yielding thread: " << name); DEBUG(dbgThread, "Yielding thread: " << name);
nextThread = kernel->scheduler->FindNextToRun(); nextThread = kernel->scheduler->FindNextToRun();
if (nextThread != NULL) { if (nextThread != NULL)
kernel->scheduler->ReadyToRun(this); {
kernel->scheduler->Run(nextThread, FALSE); kernel->scheduler->ReadyToRun(this);
kernel->scheduler->Run(nextThread, FALSE);
} }
(void) kernel->interrupt->SetLevel(oldLevel); (void)kernel->interrupt->SetLevel(oldLevel);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Thread::Sleep // Thread::Sleep
// Relinquish the CPU, because the current thread has either // Relinquish the CPU, because the current thread has either
// finished or is blocked waiting on a synchronization // finished or is blocked waiting on a synchronization
// variable (Semaphore, Lock, or Condition). In the latter case, // variable (Semaphore, Lock, or Condition). In the latter case,
// eventually some thread will wake this thread up, and put it // eventually some thread will wake this thread up, and put it
// back on the ready queue, so that it can be re-scheduled. // back on the ready queue, so that it can be re-scheduled.
@@ -231,38 +228,38 @@ Thread::Yield ()
// //
// NOTE: we assume interrupts are already disabled, because it // NOTE: we assume interrupts are already disabled, because it
// is called from the synchronization routines which must // is called from the synchronization routines which must
// disable interrupts for atomicity. We need interrupts off // disable interrupts for atomicity. We need interrupts off
// so that there can't be a time slice between pulling the first thread // so that there can't be a time slice between pulling the first thread
// off the ready list, and switching to it. // off the ready list, and switching to it.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::Sleep(bool finishing)
Thread::Sleep (bool finishing)
{ {
Thread *nextThread; Thread *nextThread;
ASSERT(this == kernel->currentThread); ASSERT(this == kernel->currentThread);
ASSERT(kernel->interrupt->getLevel() == IntOff); ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Sleeping thread: " << name); DEBUG(dbgThread, "Sleeping thread: " << name);
status = BLOCKED; status = BLOCKED;
//cout << "debug Thread::Sleep " << name << "wait for Idle\n"; // cout << "debug Thread::Sleep " << name << "wait for Idle\n";
while ((nextThread = kernel->scheduler->FindNextToRun()) == NULL) { while ((nextThread = kernel->scheduler->FindNextToRun()) == NULL)
kernel->interrupt->Idle(); // no one to run, wait for an interrupt {
} kernel->interrupt->Idle(); // no one to run, wait for an interrupt
}
// returns when it's time for us to run // returns when it's time for us to run
kernel->scheduler->Run(nextThread, finishing); kernel->scheduler->Run(nextThread, finishing);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// ThreadBegin, ThreadFinish, ThreadPrint // ThreadBegin, ThreadFinish, ThreadPrint
// Dummy functions because C++ does not (easily) allow pointers to member // Dummy functions because C++ does not (easily) allow pointers to member
// functions. So we create a dummy C function // functions. So we create a dummy C function
// (which we can pass a pointer to), that then simply calls the // (which we can pass a pointer to), that then simply calls the
// member function. // member function.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
static void ThreadFinish() { kernel->currentThread->Finish(); } static void ThreadFinish() { kernel->currentThread->Finish(); }
static void ThreadBegin() { kernel->currentThread->Begin(); } static void ThreadBegin() { kernel->currentThread->Begin(); }
void ThreadPrint(Thread *t) { t->Print(); } void ThreadPrint(Thread *t) { t->Print(); }
@@ -277,13 +274,16 @@ void ThreadPrint(Thread *t) { t->Print(); }
static void * static void *
PLabelToAddr(void *plabel) PLabelToAddr(void *plabel)
{ {
int funcPtr = (int) plabel; int funcPtr = (int)plabel;
if (funcPtr & 0x02) { if (funcPtr & 0x02)
{
// L-Field is set. This is a PLT pointer // L-Field is set. This is a PLT pointer
funcPtr -= 2; // Get rid of the L bit funcPtr -= 2; // Get rid of the L bit
return (*(void **)funcPtr); return (*(void **)funcPtr);
} else { }
else
{
// L-field not set. // L-field not set.
return plabel; return plabel;
} }
@@ -302,50 +302,48 @@ PLabelToAddr(void *plabel)
// "arg" is the parameter to be passed to the procedure // "arg" is the parameter to be passed to the procedure
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::StackAllocate(VoidFunctionPtr func, void *arg)
Thread::StackAllocate (VoidFunctionPtr func, void *arg)
{ {
stack = (int *) AllocBoundedArray(StackSize * sizeof(int)); stack = (int *)AllocBoundedArray(StackSize * sizeof(int));
#ifdef PARISC #ifdef PARISC
// HP stack works from low addresses to high addresses // HP stack works from low addresses to high addresses
// everyone else works the other way: from high addresses to low addresses // everyone else works the other way: from high addresses to low addresses
stackTop = stack + 16; // HP requires 64-byte frame marker stackTop = stack + 16; // HP requires 64-byte frame marker
stack[StackSize - 1] = STACK_FENCEPOST; stack[StackSize - 1] = STACK_FENCEPOST;
#endif #endif
#ifdef SPARC #ifdef SPARC
stackTop = stack + StackSize - 96; // SPARC stack must contains at stackTop = stack + StackSize - 96; // SPARC stack must contains at
// least 1 activation record // least 1 activation record
// to start with. // to start with.
*stack = STACK_FENCEPOST; *stack = STACK_FENCEPOST;
#endif #endif
#ifdef PowerPC // RS6000 #ifdef PowerPC // RS6000
stackTop = stack + StackSize - 16; // RS6000 requires 64-byte frame marker stackTop = stack + StackSize - 16; // RS6000 requires 64-byte frame marker
*stack = STACK_FENCEPOST; *stack = STACK_FENCEPOST;
#endif #endif
#ifdef DECMIPS #ifdef DECMIPS
stackTop = stack + StackSize - 4; // -4 to be on the safe side! stackTop = stack + StackSize - 4; // -4 to be on the safe side!
*stack = STACK_FENCEPOST; *stack = STACK_FENCEPOST;
#endif #endif
#ifdef ALPHA #ifdef ALPHA
stackTop = stack + StackSize - 8; // -8 to be on the safe side! stackTop = stack + StackSize - 8; // -8 to be on the safe side!
*stack = STACK_FENCEPOST; *stack = STACK_FENCEPOST;
#endif #endif
#ifdef x86 #ifdef x86
// the x86 passes the return address on the stack. In order for SWITCH() // 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 // to go to ThreadRoot when we switch to this thread, the return addres
// used in SWITCH() must be the starting address of ThreadRoot. // used in SWITCH() must be the starting address of ThreadRoot.
stackTop = stack + StackSize - 4; // -4 to be on the safe side! stackTop = stack + StackSize - 4; // -4 to be on the safe side!
*(--stackTop) = (int) ThreadRoot; *(--stackTop) = (int)ThreadRoot;
*stack = STACK_FENCEPOST; *stack = STACK_FENCEPOST;
#endif #endif
#ifdef PARISC #ifdef PARISC
machineState[PCState] = PLabelToAddr(ThreadRoot); machineState[PCState] = PLabelToAddr(ThreadRoot);
machineState[StartupPCState] = PLabelToAddr(ThreadBegin); machineState[StartupPCState] = PLabelToAddr(ThreadBegin);
@@ -353,11 +351,11 @@ Thread::StackAllocate (VoidFunctionPtr func, void *arg)
machineState[InitialArgState] = arg; machineState[InitialArgState] = arg;
machineState[WhenDonePCState] = PLabelToAddr(ThreadFinish); machineState[WhenDonePCState] = PLabelToAddr(ThreadFinish);
#else #else
machineState[PCState] = (void*)ThreadRoot; machineState[PCState] = (void *)ThreadRoot;
machineState[StartupPCState] = (void*)ThreadBegin; machineState[StartupPCState] = (void *)ThreadBegin;
machineState[InitialPCState] = (void*)func; machineState[InitialPCState] = (void *)func;
machineState[InitialArgState] = (void*)arg; machineState[InitialArgState] = (void *)arg;
machineState[WhenDonePCState] = (void*)ThreadFinish; machineState[WhenDonePCState] = (void *)ThreadFinish;
#endif #endif
} }
@@ -367,38 +365,35 @@ Thread::StackAllocate (VoidFunctionPtr func, void *arg)
// Thread::SaveUserState // Thread::SaveUserState
// Save the CPU state of a user program on a context switch. // Save the CPU state of a user program on a context switch.
// //
// Note that a user program thread has *two* sets of CPU registers -- // Note that a user program thread has *two* sets of CPU registers --
// one for its state while executing user code, one for its state // one for its state while executing user code, one for its state
// while executing kernel code. This routine saves the former. // while executing kernel code. This routine saves the former.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::SaveUserState()
Thread::SaveUserState()
{ {
for (int i = 0; i < NumTotalRegs; i++) for (int i = 0; i < NumTotalRegs; i++)
userRegisters[i] = kernel->machine->ReadRegister(i); userRegisters[i] = kernel->machine->ReadRegister(i);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Thread::RestoreUserState // Thread::RestoreUserState
// Restore the CPU state of a user program on a context switch. // Restore the CPU state of a user program on a context switch.
// //
// Note that a user program thread has *two* sets of CPU registers -- // Note that a user program thread has *two* sets of CPU registers --
// one for its state while executing user code, one for its state // one for its state while executing user code, one for its state
// while executing kernel code. This routine restores the former. // while executing kernel code. This routine restores the former.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::RestoreUserState()
Thread::RestoreUserState()
{ {
for (int i = 0; i < NumTotalRegs; i++) for (int i = 0; i < NumTotalRegs; i++)
kernel->machine->WriteRegister(i, userRegisters[i]); kernel->machine->WriteRegister(i, userRegisters[i]);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// SimpleThread // SimpleThread
// Loop 5 times, yielding the CPU to another ready thread // Loop 5 times, yielding the CPU to another ready thread
// each iteration. // each iteration.
// //
// "which" is simply a number identifying the thread, for debugging // "which" is simply a number identifying the thread, for debugging
@@ -409,27 +404,40 @@ static void
SimpleThread(int which) SimpleThread(int which)
{ {
int num; int num;
for (num = 0; num < 5; num++) { for (num = 0; num < 5; num++)
cout << "*** thread " << which << " looped " << num << " times\n"; {
cout << "*** thread " << which << " looped " << num << " times\n";
kernel->currentThread->Yield(); kernel->currentThread->Yield();
} }
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Thread::SelfTest // Thread::SelfTest
// Set up a ping-pong between two threads, by forking a thread // Set up a ping-pong between two threads, by forking a thread
// to call SimpleThread, and then calling SimpleThread ourselves. // to call SimpleThread, and then calling SimpleThread ourselves.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void Thread::SelfTest()
Thread::SelfTest()
{ {
DEBUG(dbgThread, "Entering Thread::SelfTest"); DEBUG(dbgThread, "Entering Thread::SelfTest");
Thread *t = new Thread("forked thread", 1); Thread *t = new Thread("forked thread", 1);
t->Fork((VoidFunctionPtr) SimpleThread, (void *) 1); t->Fork((VoidFunctionPtr)SimpleThread, (void *)1);
kernel->currentThread->Yield(); kernel->currentThread->Yield();
SimpleThread(0); SimpleThread(0);
} }
// Todo ----
int Thread::getPriority() const
{
return priority;
}
void Thread::setPriority(int p)
{
ASSERT(p >= 0 && p <= 149);
priority = p;
}
// ---------

View File

@@ -80,6 +80,10 @@ class Thread {
int *stackTop; // the current stack pointer int *stackTop; // the current stack pointer
void *machineState[MachineStateSize]; // all registers except for stackTop void *machineState[MachineStateSize]; // all registers except for stackTop
// Todo ----
int priority;
// ---------
public: public:
Thread(char* debugName, int threadID); // initialize a Thread Thread(char* debugName, int threadID); // initialize a Thread
~Thread(); // deallocate a Thread ~Thread(); // deallocate a Thread
@@ -89,6 +93,11 @@ class Thread {
// basic thread operations // basic thread operations
// Todo ----
int getPriority() const;
void setPriority(int p);
// ---------
void Fork(VoidFunctionPtr func, void *arg); void Fork(VoidFunctionPtr func, void *arg);
// Make thread run (*func)(arg) // Make thread run (*func)(arg)
void Yield(); // Relinquish the CPU if any void Yield(); // Relinquish the CPU if any
@@ -97,32 +106,32 @@ class Thread {
// relinquish the processor // relinquish the processor
void Begin(); // Startup code for the thread void Begin(); // Startup code for the thread
void Finish(); // The thread is done executing void Finish(); // The thread is done executing
void CheckOverflow(); // Check if thread stack has overflowed void CheckOverflow(); // Check if thread stack has overflowed
void setStatus(ThreadStatus st) { status = st; } void setStatus(ThreadStatus st) { status = st; }
ThreadStatus getStatus() { return (status); } ThreadStatus getStatus() { return (status); }
char* getName() { return (name); } char* getName() { return (name); }
int getID() { return (ID); } int getID() { return (ID); }
void Print() { cout << name; } void Print() { cout << name; }
void SelfTest(); // test whether thread impl is working void SelfTest(); // test whether thread impl is working
private: private:
// some of the private data for this class is listed above // some of the private data for this class is listed above
int *stack; // Bottom of the stack int *stack; // Bottom of the stack
// NULL if this is the main thread // NULL if this is the main thread
// (If NULL, don't deallocate stack) // (If NULL, don't deallocate stack)
ThreadStatus status; // ready, running or blocked ThreadStatus status; // ready, running or blocked
char* name; char* name;
int ID; int ID;
void StackAllocate(VoidFunctionPtr func, void *arg); void StackAllocate(VoidFunctionPtr func, void *arg);
// Allocate a stack for thread. // Allocate a stack for thread.
// Used internally by Fork() // Used internally by Fork()
// A thread running a user program actually has *two* sets of CPU registers -- // 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 // one for its state while executing user code, one for its state
// while executing kernel code. // while executing kernel code.
int userRegisters[NumTotalRegs]; // user-level CPU register state int userRegisters[NumTotalRegs]; // user-level CPU register state

View File

@@ -27,9 +27,8 @@
// object file header, in case the file was generated on a little // object file header, in case the file was generated on a little
// endian machine, and we're now running on a big endian machine. // endian machine, and we're now running on a big endian machine.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
static void
static void SwapHeader(NoffHeader* noffH)
SwapHeader (NoffHeader *noffH)
{ {
noffH->noffMagic = WordToHost(noffH->noffMagic); noffH->noffMagic = WordToHost(noffH->noffMagic);
noffH->code.size = WordToHost(noffH->code.size); noffH->code.size = WordToHost(noffH->code.size);
@@ -37,10 +36,10 @@ SwapHeader (NoffHeader *noffH)
noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr); noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr);
#ifdef RDATA #ifdef RDATA
noffH->readonlyData.size = WordToHost(noffH->readonlyData.size); noffH->readonlyData.size = WordToHost(noffH->readonlyData.size);
noffH->readonlyData.virtualAddr = noffH->readonlyData.virtualAddr =
WordToHost(noffH->readonlyData.virtualAddr); WordToHost(noffH->readonlyData.virtualAddr);
noffH->readonlyData.inFileAddr = noffH->readonlyData.inFileAddr =
WordToHost(noffH->readonlyData.inFileAddr); WordToHost(noffH->readonlyData.inFileAddr);
#endif #endif
noffH->initData.size = WordToHost(noffH->initData.size); noffH->initData.size = WordToHost(noffH->initData.size);
noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr); noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr);
@@ -50,10 +49,10 @@ SwapHeader (NoffHeader *noffH)
noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr); noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr);
#ifdef RDATA #ifdef RDATA
DEBUG(dbgAddr, "code = " << noffH->code.size << DEBUG(dbgAddr, "code = " << noffH->code.size <<
" readonly = " << noffH->readonlyData.size << " readonly = " << noffH->readonlyData.size <<
" init = " << noffH->initData.size << " init = " << noffH->initData.size <<
" uninit = " << noffH->uninitData.size << "\n"); " uninit = " << noffH->uninitData.size << "\n");
#endif #endif
} }
@@ -64,21 +63,23 @@ SwapHeader (NoffHeader *noffH)
// memory. For now, this is really simple (1:1), since we are // memory. For now, this is really simple (1:1), since we are
// only uniprogramming, and we have a single unsegmented page table // only uniprogramming, and we have a single unsegmented page table
//---------------------------------------------------------------------- //----------------------------------------------------------------------
AddrSpace::AddrSpace() AddrSpace::AddrSpace()
{ {
pageTable = new TranslationEntry[NumPhysPages]; pageTable = NULL; // initialize with NULL
for (int i = 0; i < NumPhysPages; i++) { /*
pageTable[i].virtualPage = i; // for now, virt page # = phys page # pageTable = new TranslationEntry[NumPhysPages];
pageTable[i].physicalPage = i; for (int i = 0; i < NumPhysPages; i++) {
pageTable[i].valid = TRUE; pageTable[i].virtualPage = i; // for now, virt page # = phys page #
pageTable[i].use = FALSE; pageTable[i].physicalPage = i;
pageTable[i].dirty = FALSE; pageTable[i].valid = TRUE;
pageTable[i].readOnly = FALSE; pageTable[i].use = FALSE;
} pageTable[i].dirty = FALSE;
pageTable[i].readOnly = FALSE;
// zero out the entire address space }
bzero(kernel->machine->mainMemory, MemorySize);
// zero out the entire address space
bzero(kernel->machine->mainMemory, MemorySize);
*/
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@@ -88,7 +89,9 @@ AddrSpace::AddrSpace()
AddrSpace::~AddrSpace() AddrSpace::~AddrSpace()
{ {
delete pageTable; /* delete pageTable; */
// release frame table by page table
kernel->frameTable->Release(pageTable, numPages);
} }
@@ -101,71 +104,135 @@ AddrSpace::~AddrSpace()
// //
// "fileName" is the file containing the object code to load into memory // "fileName" is the file containing the object code to load into memory
//---------------------------------------------------------------------- //----------------------------------------------------------------------
bool
bool AddrSpace::Load(char* fileName)
AddrSpace::Load(char *fileName)
{ {
OpenFile *executable = kernel->fileSystem->Open(fileName); OpenFile* executable = kernel->fileSystem->Open(fileName);
NoffHeader noffH; NoffHeader noffH;
unsigned int size; unsigned int size;
if (executable == NULL) { if (executable == NULL) {
cerr << "Unable to open file " << fileName << "\n"; cerr << "Unable to open file " << fileName << "\n";
return FALSE; return FALSE;
} }
executable->ReadAt((char *)&noffH, sizeof(noffH), 0); executable->ReadAt((char*)&noffH, sizeof(noffH), 0);
if ((noffH.noffMagic != NOFFMAGIC) && if ((noffH.noffMagic != NOFFMAGIC) &&
(WordToHost(noffH.noffMagic) == NOFFMAGIC)) (WordToHost(noffH.noffMagic) == NOFFMAGIC))
SwapHeader(&noffH); SwapHeader(&noffH);
ASSERT(noffH.noffMagic == NOFFMAGIC); ASSERT(noffH.noffMagic == NOFFMAGIC);
#ifdef RDATA #ifdef RDATA
// how big is address space? // how big is address space?
size = noffH.code.size + noffH.readonlyData.size + noffH.initData.size + size = noffH.code.size + noffH.readonlyData.size + noffH.initData.size +
noffH.uninitData.size + UserStackSize; noffH.uninitData.size + UserStackSize;
// we need to increase the size // we need to increase the size
// to leave room for the stack // to leave room for the stack
#else #else
// how big is address space? // how big is address space?
size = noffH.code.size + noffH.initData.size + noffH.uninitData.size size = noffH.code.size + noffH.initData.size + noffH.uninitData.size
+ UserStackSize; // we need to increase the size + UserStackSize; // we need to increase the size
// to leave room for the stack // to leave room for the stack
#endif #endif
numPages = divRoundUp(size, PageSize); numPages = divRoundUp(size, PageSize);
size = numPages * PageSize; size = numPages * PageSize;
ASSERT(numPages <= NumPhysPages); // check we're not trying pageTable = kernel->frameTable->Allocate(numPages);
// to run anything too big -- if (!pageTable) {
// at least until we have kernel->interrupt->setStatus(SystemMode);
// virtual memory ExceptionHandler(MemoryLimitException);
kernel->interrupt->setStatus(UserMode);
}
DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size); DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size);
// then, copy in the code and data segments into memory // then, copy in the code and data segments into memory
// Note: this code assumes that virtual address = physical address uint paddr; // physical address
ExceptionType ex; // occurring exception
int sz, // total size to load
vaddr, // base virtual address
fpos, // base file position
to_load; // size to load on each time
if (noffH.code.size > 0) { if (noffH.code.size > 0) {
DEBUG(dbgAddr, "Initializing code segment."); DEBUG(dbgAddr, "Initializing code segment.");
DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size); DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.code.virtualAddr]), sz = noffH.code.size;
noffH.code.size, noffH.code.inFileAddr); vaddr = noffH.code.virtualAddr;
fpos = noffH.code.inFileAddr;
for (uint offset = 0; offset < sz; offset += PageSize) {
ex = Translate(vaddr + offset, &paddr, 1);
if (ex != NoException) {
kernel->interrupt->setStatus(SystemMode);
ExceptionHandler(ex);
kernel->interrupt->setStatus(UserMode);
}
to_load = offset + PageSize < sz ? PageSize : sz - offset;
executable->ReadAt(
&(kernel->machine->mainMemory[paddr]),
to_load, fpos + offset);
}
} }
if (noffH.initData.size > 0) { if (noffH.initData.size > 0) {
DEBUG(dbgAddr, "Initializing data segment."); DEBUG(dbgAddr, "Initializing data segment.");
DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size); DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.initData.virtualAddr]), sz = noffH.initData.size;
noffH.initData.size, noffH.initData.inFileAddr); vaddr = noffH.initData.virtualAddr;
fpos = noffH.initData.inFileAddr;
for (uint offset = 0; offset < sz; offset += PageSize) {
ex = Translate(vaddr + offset, &paddr, 1);
if (ex != NoException) {
kernel->interrupt->setStatus(SystemMode);
ExceptionHandler(ex);
kernel->interrupt->setStatus(UserMode);
}
to_load = offset + PageSize < sz ? PageSize : sz - offset;
executable->ReadAt(
&(kernel->machine->mainMemory[paddr]),
to_load, fpos + offset);
}
} }
#ifdef RDATA #ifdef RDATA
if (noffH.readonlyData.size > 0) { if (noffH.readonlyData.size > 0) {
DEBUG(dbgAddr, "Initializing read only data segment."); DEBUG(dbgAddr, "Initializing read only data segment.");
DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size); DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.readonlyData.virtualAddr]), sz = noffH.readonlyData.size;
noffH.readonlyData.size, noffH.readonlyData.inFileAddr); vaddr = noffH.readonlyData.virtualAddr;
fpos = noffH.readonlyData.inFileAddr;
// read only flag for page table
for (int i = 0, lim = divRoundUp(sz, PageSize),
from = vaddr / PageSize; i < lim; ++i)
pageTable[from + i].readOnly = TRUE;
for (uint offset = 0; offset < sz; offset += PageSize) {
ex = Translate(vaddr + offset, &paddr, 0); // read only
if (ex != NoException) {
kernel->interrupt->setStatus(SystemMode);
ExceptionHandler(ex);
kernel->interrupt->setStatus(UserMode);
}
to_load = offset + PageSize < sz ? PageSize : sz - offset;
executable->ReadAt(
&(kernel->machine->mainMemory[paddr]),
to_load, fpos + offset);
}
} }
#endif #endif
@@ -181,9 +248,8 @@ AddrSpace::Load(char *fileName)
// the address space // the address space
// //
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void
void AddrSpace::Execute(char* fileName)
AddrSpace::Execute(char* fileName)
{ {
kernel->currentThread->space = this; kernel->currentThread->space = this;
@@ -191,11 +257,11 @@ AddrSpace::Execute(char* fileName)
this->InitRegisters(); // set the initial register values this->InitRegisters(); // set the initial register values
this->RestoreState(); // load page table register this->RestoreState(); // load page table register
kernel->machine->Run(); // jump to the user progam kernel->machine->Run(); // jump to the user program
ASSERTNOTREACHED(); // machine->Run never returns; ASSERTNOTREACHED(); // machine->Run never returns;
// the address space exits // the address space exits
// by doing the syscall "exit" // by doing the syscall "exit"
} }
@@ -208,19 +274,18 @@ AddrSpace::Execute(char* fileName)
// will be saved/restored into the currentThread->userRegisters // will be saved/restored into the currentThread->userRegisters
// when this thread is context switched out. // when this thread is context switched out.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void void
AddrSpace::InitRegisters() AddrSpace::InitRegisters()
{ {
Machine *machine = kernel->machine; Machine* machine = kernel->machine;
int i; int i;
for (i = 0; i < NumTotalRegs; i++) for (i = 0; i < NumTotalRegs; i++)
machine->WriteRegister(i, 0); machine->WriteRegister(i, 0);
// Initial program counter -- must be location of "Start", which // Initial program counter -- must be location of "Start", which
// is assumed to be virtual address zero // is assumed to be virtual address zero
machine->WriteRegister(PCReg, 0); machine->WriteRegister(PCReg, 0);
// Need to also tell MIPS where next instruction is, because // Need to also tell MIPS where next instruction is, because
// of branch delay possibility // of branch delay possibility
@@ -228,9 +293,9 @@ AddrSpace::InitRegisters()
// after start will be at virtual address four. // after start will be at virtual address four.
machine->WriteRegister(NextPCReg, 4); machine->WriteRegister(NextPCReg, 4);
// Set the stack register to the end of the address space, where we // 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 // allocated the stack; but subtract off a bit, to make sure we don't
// accidentally reference off the end! // accidentally reference off the end!
machine->WriteRegister(StackReg, numPages * PageSize - 16); machine->WriteRegister(StackReg, numPages * PageSize - 16);
DEBUG(dbgAddr, "Initializing stack pointer: " << numPages * PageSize - 16); DEBUG(dbgAddr, "Initializing stack pointer: " << numPages * PageSize - 16);
} }
@@ -242,9 +307,9 @@ AddrSpace::InitRegisters()
// //
// For now, don't need to save anything! // For now, don't need to save anything!
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void AddrSpace::SaveState()
void AddrSpace::SaveState() {
{} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// AddrSpace::RestoreState // AddrSpace::RestoreState
@@ -253,8 +318,7 @@ void AddrSpace::SaveState()
// //
// For now, tell the machine where to find the page table. // For now, tell the machine where to find the page table.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void AddrSpace::RestoreState()
void AddrSpace::RestoreState()
{ {
kernel->machine->pageTable = pageTable; kernel->machine->pageTable = pageTable;
kernel->machine->pageTableSize = numPages; kernel->machine->pageTableSize = numPages;
@@ -270,20 +334,20 @@ void AddrSpace::RestoreState()
// Return any exceptions caused by the address translation. // Return any exceptions caused by the address translation.
//---------------------------------------------------------------------- //----------------------------------------------------------------------
ExceptionType ExceptionType
AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite) AddrSpace::Translate(unsigned int vaddr, unsigned int* paddr, int isReadWrite)
{ {
TranslationEntry *pte; TranslationEntry* pte;
int pfn; int pfn;
unsigned int vpn = vaddr / PageSize; unsigned int vpn = vaddr / PageSize;
unsigned int offset = vaddr % PageSize; unsigned int offset = vaddr % PageSize;
if(vpn >= numPages) { if (vpn >= numPages) {
return AddressErrorException; return AddressErrorException;
} }
pte = &pageTable[vpn]; pte = &pageTable[vpn];
if(isReadWrite && pte->readOnly) { if (isReadWrite && pte->readOnly) {
return ReadOnlyException; return ReadOnlyException;
} }
@@ -298,10 +362,10 @@ AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
pte->use = TRUE; // set the use, dirty bits pte->use = TRUE; // set the use, dirty bits
if(isReadWrite) if (isReadWrite)
pte->dirty = TRUE; pte->dirty = TRUE;
*paddr = pfn*PageSize + offset; *paddr = pfn * PageSize + offset;
ASSERT((*paddr < MemorySize)); ASSERT((*paddr < MemorySize));
@@ -311,6 +375,43 @@ AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
return NoException; return NoException;
} }
FrameTable::FrameTable() {
for (int i = 0; i < NumPhysPages; ++i)
available.Append(i);
}
FrameTable::~FrameTable() {}
uint FrameTable::RemainSize() { return available.NumInList(); }
PageTable FrameTable::Allocate(uint pageNum) {
// if not enough memory
if (RemainSize() < pageNum)
return NULL;
PageTable ptb = new TranslationEntry[pageNum];
for (int i = 0; i < pageNum; ++i) {
ptb[i].virtualPage = i;
int f = available.RemoveFront(); // frame number
ptb[i].physicalPage = f;
// initialize flags
ptb[i].valid = TRUE;
ptb[i].use = FALSE;
ptb[i].dirty = FALSE;
ptb[i].readOnly = FALSE;
// zero out the entire address space
bzero(kernel->machine->mainMemory + f * PageSize, PageSize);
}
return ptb;
}
void FrameTable::Release(PageTable ptb, int pageNum) {
if (!ptb)
return; // nothing to release
for (int i = 0; i < pageNum; ++i)
available.Append(ptb[i].physicalPage);
delete[] ptb;
}

View File

@@ -19,35 +19,83 @@
#define UserStackSize 1024 // increase this as necessary! #define UserStackSize 1024 // increase this as necessary!
class AddrSpace { class AddrSpace {
public: public:
AddrSpace(); // Create an address space. AddrSpace(); // Create an address space.
~AddrSpace(); // De-allocate an address space ~AddrSpace(); // De-allocate an address space
bool Load(char *fileName); // Load a program into addr space from bool Load(char* fileName); // Load a program into addr space from
// a file // a file
// return false if not found // return false if not found
void Execute(char *fileName); // Run a program void Execute(char* fileName); // Run a program
// assumes the program has already // assumes the program has already
// been loaded // been loaded
void SaveState(); // Save/restore address space-specific void SaveState(); // Save/restore address space-specific
void RestoreState(); // info on a context switch void RestoreState(); // info on a context switch
// Translate virtual address _vaddr_ // Translate virtual address _vaddr_
// to physical address _paddr_. _mode_ // to physical address _paddr_. _mode_
// is 0 for Read, 1 for Write. // is 0 for Read, 1 for Write.
ExceptionType Translate(unsigned int vaddr, unsigned int *paddr, int mode); ExceptionType Translate(unsigned int vaddr, unsigned int* paddr, int mode);
private: private:
TranslationEntry *pageTable; // Assume linear page table translation TranslationEntry* pageTable; // Assume linear page table translation
// for now! // for now!
unsigned int numPages; // Number of pages in the virtual unsigned int numPages; // Number of pages in the virtual
// address space // address space
void InitRegisters(); // Initialize user-level CPU registers, void InitRegisters(); // Initialize user-level CPU registers,
// before jumping to user code // before jumping to user code
}; };
#endif // ADDRSPACE_H #endif // ADDRSPACE_H
#ifndef FRAME_TABLE_H
#define FRAME_TABLE_H
#include "machine.h"
#include "list.h"
/**
* Data structure of Virtual Memory
*/
typedef TranslationEntry* PageTable;
/**
* Data structure of Physical Memory
*/
class FrameTable {
public:
/**
* Initialize a frame table
*/
FrameTable();
~FrameTable();
/**
* Allocate pageNum of frames (pages) and collect
* corresponding translation information into a page table.
*
* @param pageNum numbers of pages
* @return a new Page table, NULL if not enough memory space
*/
PageTable Allocate(uint pageNum);
/**
* Release the physical memory frame
* which the info stored in PageTable
*/
void Release(PageTable ptb, int pageNum);
/**
* @return the remaining numbers of entry of the frame table
*/
uint RemainSize();
private:
List<int> available;
};
#endif /* FRAME_TABLE_H */

View File

@@ -51,74 +51,138 @@
void void
ExceptionHandler(ExceptionType which) ExceptionHandler(ExceptionType which)
{ {
int type = kernel->machine->ReadRegister(2); int type = kernel->machine->ReadRegister(2);
int val; int val;
int status, exit, threadID, programID; int status, exit, threadID, programID;
int fd, size;
DEBUG(dbgSys, "Received Exception " << which << " type: " << type << "\n"); DEBUG(dbgSys, "Received Exception " << which << " type: " << type << "\n");
switch (which) { switch (which) {
case SyscallException: case SyscallException:
switch(type) { switch (type) {
case SC_Halt: case SC_Halt:
DEBUG(dbgSys, "Shutdown, initiated by user program.\n"); DEBUG(dbgSys, "Shutdown, initiated by user program.\n");
SysHalt(); SysHalt();
cout<<"in exception\n"; cout << "in exception\n";
ASSERTNOTREACHED(); ASSERTNOTREACHED();
break; break;
case SC_MSG: case SC_MSG:
DEBUG(dbgSys, "Message received.\n"); DEBUG(dbgSys, "Message received.\n");
val = kernel->machine->ReadRegister(4); val = kernel->machine->ReadRegister(4);
{ {
char *msg = &(kernel->machine->mainMemory[val]); char* msg = &(kernel->machine->mainMemory[val]);
cout << msg << endl; cout << msg << endl;
} }
SysHalt(); SysHalt();
ASSERTNOTREACHED(); ASSERTNOTREACHED();
break; break;
case SC_PrintInt:
val = kernel->machine->ReadRegister(4);
SysPrintInt(val);
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;
case SC_Create: case SC_Create:
val = kernel->machine->ReadRegister(4); val = kernel->machine->ReadRegister(4);
{ {
char *filename = &(kernel->machine->mainMemory[val]); char* filename = &(kernel->machine->mainMemory[val]);
//cout << filename << endl; //cout << filename << endl;
status = SysCreate(filename); status = SysCreate(filename);
kernel->machine->WriteRegister(2, (int) status); kernel->machine->WriteRegister(2, (int)status);
} }
kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg)); kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4); kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg)+4); kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4);
return; return;
ASSERTNOTREACHED(); ASSERTNOTREACHED();
break; break;
case SC_Add: case SC_Open:
val = kernel->machine->ReadRegister(4);
{
char* filename = &(kernel->machine->mainMemory[val]);
//cout << filename << endl;
fd = SysOpen(filename);
kernel->machine->WriteRegister(2, (int)fd);
}
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_Write:
val = kernel->machine->ReadRegister(4);
size = kernel->machine->ReadRegister(5);
fd = kernel->machine->ReadRegister(6);
{
char* buffer = &(kernel->machine->mainMemory[val]);
size = SysWrite(buffer, size, fd);
kernel->machine->WriteRegister(2, (int)size);
}
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_Close:
fd = kernel->machine->ReadRegister(4);
{
val = SysClose(fd);
kernel->machine->WriteRegister(2, (int)val);
}
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_Read:
val = kernel->machine->ReadRegister(4);
size = kernel->machine->ReadRegister(5);
fd = kernel->machine->ReadRegister(6);
{
char* buffer = &(kernel->machine->mainMemory[val]);
size = SysRead(buffer, size, fd);
kernel->machine->WriteRegister(2, (int)size);
}
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"); DEBUG(dbgSys, "Add " << kernel->machine->ReadRegister(4) << " + " << kernel->machine->ReadRegister(5) << "\n");
/* Process SysAdd Systemcall*/ /* Process SysAdd Systemcall*/
int result; int result;
result = SysAdd(/* int op1 */(int)kernel->machine->ReadRegister(4), result = SysAdd(/* int op1 */(int)kernel->machine->ReadRegister(4),
/* int op2 */(int)kernel->machine->ReadRegister(5)); /* int op2 */(int)kernel->machine->ReadRegister(5));
DEBUG(dbgSys, "Add returning with " << result << "\n"); DEBUG(dbgSys, "Add returning with " << result << "\n");
/* Prepare Result */ /* Prepare Result */
kernel->machine->WriteRegister(2, (int)result); kernel->machine->WriteRegister(2, (int)result);
/* Modify return point */ /* Modify return point */
{ {
/* set previous programm counter (debugging only)*/ /* set previous programm counter (debugging only)*/
kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg)); kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
/* set programm counter to next instruction (all Instructions are 4 byte wide)*/ /* set programm counter to next instruction (all Instructions are 4 byte wide)*/
kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4); kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
/* set next programm counter for brach execution */ /* set next programm counter for brach execution */
kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg)+4); kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4);
} }
cout << "result is " << result << "\n"; cout << "result is " << result << "\n";
return; return;
ASSERTNOTREACHED(); ASSERTNOTREACHED();
break; break;
case SC_Exit: case SC_Exit:
DEBUG(dbgAddr, "Program exit\n"); DEBUG(dbgAddr, "Program exit\n");
val=kernel->machine->ReadRegister(4); val = kernel->machine->ReadRegister(4);
cout << "return value:" << val << endl; cout << "return value:" << val << endl;
kernel->currentThread->Finish(); kernel->currentThread->Finish();
break; break;
default: default:
cerr << "Unexpected system call " << type << "\n"; cerr << "Unexpected system call " << type << "\n";
break; break;
} }
@@ -126,7 +190,7 @@ ExceptionHandler(ExceptionType which)
default: default:
cerr << "Unexpected user mode exception " << (int)which << "\n"; cerr << "Unexpected user mode exception " << (int)which << "\n";
break; break;
} }
ASSERTNOTREACHED(); ASSERTNOTREACHED();
} }

View File

@@ -0,0 +1,11 @@
#include "syscall.h"
int
main()
{
int n;
for (n = 0; n < 4; n++) {
PrintInt(1);
}
return 0;
}

View File

@@ -0,0 +1,11 @@
#include "syscall.h"
int
main()
{
int n;
for (n = 0; n < 5; n++) {
PrintInt(2);
}
return 0;
}

View File

@@ -0,0 +1,12 @@
#include "syscall.h"
int
main()
{
int n;
for (n = 0; n < 12; n++) {
PrintInt(3);
}
return 0;
}

View File

@@ -0,0 +1,12 @@
#include "syscall.h"
int
main()
{
int n;
for (n = 0; n < 11; n++) {
PrintInt(4);
}
return 0;
}

View File

@@ -2,7 +2,7 @@
* *
* userprog/ksyscall.h * userprog/ksyscall.h
* *
* Kernel interface for systemcalls * Kernel interface for systemcalls
* *
* by Marcus Voelp (c) Universitaet Karlsruhe * by Marcus Voelp (c) Universitaet Karlsruhe
* *
@@ -15,18 +15,19 @@
#include "synchconsole.h" #include "synchconsole.h"
typedef int OpenFileId;
void SysHalt() void SysHalt()
{ {
kernel->interrupt->Halt(); kernel->interrupt->Halt();
} }
int SysAdd(int op1, int op2) int SysAdd(int op1, int op2)
{ {
return op1 + op2; return op1 + op2;
} }
int SysCreate(char *filename) int SysCreate(char* filename)
{ {
// return value // return value
// 1: success // 1: success
@@ -34,5 +35,37 @@ int SysCreate(char *filename)
return kernel->interrupt->CreateFile(filename); return kernel->interrupt->CreateFile(filename);
} }
void SysPrintInt(int value)
{
kernel->interrupt->PrintInt(value);
}
// -1: open fail
// fd
OpenFileId SysOpen(char* filename)
{
return kernel->interrupt->OpenFile(filename);
}
// -1: write fail
// size
int SysWrite(char* buffer, int size, OpenFileId fd)
{
return kernel->interrupt->WriteFile(buffer, size, fd);
}
// 1: close success
// 0: close fail
int SysClose(OpenFileId fd)
{
return kernel->interrupt->CloseFile(fd);
}
// -1: read fail
// size
int SysRead(char* buffer, int size, OpenFileId fd)
{
return kernel->interrupt->ReadFile(buffer, size, fd);
}
#endif /* ! __USERPROG_KSYSCALL_H__ */ #endif /* ! __USERPROG_KSYSCALL_H__ */

View File

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

View File

@@ -17,7 +17,7 @@
// otherwise, read from this file // otherwise, read from this file
//---------------------------------------------------------------------- //----------------------------------------------------------------------
SynchConsoleInput::SynchConsoleInput(char *inputFile) SynchConsoleInput::SynchConsoleInput(char* inputFile)
{ {
consoleInput = new ConsoleInput(inputFile, this); consoleInput = new ConsoleInput(inputFile, this);
lock = new Lock("console in"); lock = new Lock("console in");
@@ -30,9 +30,9 @@ SynchConsoleInput::SynchConsoleInput(char *inputFile)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
SynchConsoleInput::~SynchConsoleInput() SynchConsoleInput::~SynchConsoleInput()
{ {
delete consoleInput; delete consoleInput;
delete lock; delete lock;
delete waitFor; delete waitFor;
} }
@@ -73,7 +73,7 @@ SynchConsoleInput::CallBack()
// otherwise, read from this file // otherwise, read from this file
//---------------------------------------------------------------------- //----------------------------------------------------------------------
SynchConsoleOutput::SynchConsoleOutput(char *outputFile) SynchConsoleOutput::SynchConsoleOutput(char* outputFile)
{ {
consoleOutput = new ConsoleOutput(outputFile, this); consoleOutput = new ConsoleOutput(outputFile, this);
lock = new Lock("console out"); lock = new Lock("console out");
@@ -86,9 +86,9 @@ SynchConsoleOutput::SynchConsoleOutput(char *outputFile)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
SynchConsoleOutput::~SynchConsoleOutput() SynchConsoleOutput::~SynchConsoleOutput()
{ {
delete consoleOutput; delete consoleOutput;
delete lock; delete lock;
delete waitFor; delete waitFor;
} }
@@ -106,6 +106,20 @@ SynchConsoleOutput::PutChar(char ch)
lock->Release(); lock->Release();
} }
//----------------------------------------------------------------------
// SynchConsoleOutput::PutInt
// Write a int to the console display, waiting if necessary.
//----------------------------------------------------------------------
void
SynchConsoleOutput::PutInt(int value)
{
lock->Acquire();
consoleOutput->PutInt(value);
waitFor->P();
lock->Release();
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// SynchConsoleOutput::CallBack // SynchConsoleOutput::CallBack
// Interrupt handler called when it's safe to send the next // Interrupt handler called when it's safe to send the next

View File

@@ -21,33 +21,34 @@
// a console device // a console device
class SynchConsoleInput : public CallBackObj { class SynchConsoleInput : public CallBackObj {
public: public:
SynchConsoleInput(char *inputFile); // Initialize the console device SynchConsoleInput(char* inputFile); // Initialize the console device
~SynchConsoleInput(); // Deallocate console device ~SynchConsoleInput(); // Deallocate console device
char GetChar(); // Read a character, waiting if necessary 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 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 { class SynchConsoleOutput : public CallBackObj {
public: public:
SynchConsoleOutput(char *outputFile); // Initialize the console device SynchConsoleOutput(char* outputFile); // Initialize the console device
~SynchConsoleOutput(); ~SynchConsoleOutput();
void PutChar(char ch); // Write a character, waiting if necessary void PutChar(char ch); // Write a character, waiting if necessary
void PutInt(int value);
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 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 #endif // SYNCHCONSOLE_H

View File

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

7
docker-compose.yaml Normal file
View File

@@ -0,0 +1,7 @@
---
services:
test:
build: .
user: ytshih
volumes:
- './:/work'