Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
7001ba4222
|
|||
|
72320ede22
|
|||
| bf78b95c9d | |||
| 5f06249b01 | |||
|
|
b4987f1f70 | ||
|
|
4912fe4736 | ||
|
|
549bc9bcdc | ||
|
|
b18dbf056f | ||
| 5b1cd5e1cf | |||
|
|
486f032cf0 | ||
|
|
ba9ef819ba | ||
|
|
0284b75ab6 |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.o
|
||||
*.coff
|
||||
16
Dockerfile
Normal file
16
Dockerfile
Normal 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
17
README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Introduction to OS 2024 HW
|
||||
|
||||
## 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
17
code/Makefile
Normal 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.
@@ -200,18 +200,18 @@ DEFINES = -DFILESYS_STUB -DRDATA -DSIM_FIX
|
||||
# break the thread system. You might want to use -fno-inline if
|
||||
# you need to call some inline functions from the debugger.
|
||||
|
||||
CFLAGS = -g -Wall $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -m32
|
||||
CFLAGS = -g -Wall $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -m32 -w
|
||||
LDFLAGS = -m32
|
||||
CPP_AS_FLAGS= -m32
|
||||
|
||||
#####################################################################
|
||||
CPP=/lib/cpp
|
||||
CC = g++ -m32 -Wno-deprecated
|
||||
LD = g++ -m32 -Wno-deprecated
|
||||
CC = g++ -m32
|
||||
LD = g++ -m32
|
||||
AS = as --32
|
||||
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
|
||||
|
||||
|
||||
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.
@@ -40,57 +40,108 @@
|
||||
#ifdef FILESYS_STUB // Temporarily implement file system calls as
|
||||
// calls to UNIX, until the real file system
|
||||
// implementation is available
|
||||
|
||||
typedef int OpenFileId;
|
||||
|
||||
class FileSystem {
|
||||
public:
|
||||
FileSystem() { for (int i = 0; i < 20; i++) fileDescriptorTable[i] = NULL; }
|
||||
public:
|
||||
FileSystem() { for (int i = 0; i < 20; i++) fileDescriptorTable[i] = NULL; }
|
||||
|
||||
bool Create(char *name) {
|
||||
int fileDescriptor = OpenForWrite(name);
|
||||
|
||||
if (fileDescriptor == -1) return FALSE;
|
||||
Close(fileDescriptor);
|
||||
return TRUE;
|
||||
bool Create(char* name) {
|
||||
int fileDescriptor = OpenForWrite(name);
|
||||
if (fileDescriptor == -1) return FALSE;
|
||||
Close(fileDescriptor);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
OpenFile* Open(char *name) {
|
||||
int fileDescriptor = OpenForReadWrite(name, FALSE);
|
||||
OpenFile* Open(char* name) {
|
||||
int fileDescriptor = OpenForReadWrite(name, FALSE);
|
||||
if (fileDescriptor == -1) return NULL;
|
||||
return new OpenFile(fileDescriptor);
|
||||
}
|
||||
|
||||
if (fileDescriptor == -1) return NULL;
|
||||
return new OpenFile(fileDescriptor);
|
||||
}
|
||||
OpenFileId OpenFiles(char* name) {
|
||||
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
|
||||
class FileSystem {
|
||||
public:
|
||||
FileSystem(bool format); // Initialize the file system.
|
||||
// Must be called *after* "synchDisk"
|
||||
// has been initialized.
|
||||
// If "format", there is nothing on
|
||||
// the disk, so initialize the directory
|
||||
// and the bitmap of free blocks.
|
||||
public:
|
||||
FileSystem(bool format); // Initialize the file system.
|
||||
// Must be called *after* "synchDisk"
|
||||
// has been initialized.
|
||||
// If "format", there is nothing on
|
||||
// the disk, so initialize the directory
|
||||
// and the bitmap of free blocks.
|
||||
|
||||
bool Create(char *name, int initialSize);
|
||||
// Create a file (UNIX creat)
|
||||
bool Create(char* name, int initialSize);
|
||||
// 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:
|
||||
OpenFile* freeMapFile; // Bit map of free disk blocks,
|
||||
// represented as a file
|
||||
OpenFile* directoryFile; // "Root" directory -- list of
|
||||
// file names, represented as a file
|
||||
private:
|
||||
OpenFile* freeMapFile; // Bit map of free disk blocks,
|
||||
// represented as a file
|
||||
OpenFile* directoryFile; // "Root" directory -- list of
|
||||
// file names, represented as a file
|
||||
};
|
||||
|
||||
#endif // FILESYS
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#ifdef FILESYS_STUB // Temporarily implement calls to
|
||||
// Nachos file system as calls to UNIX!
|
||||
// See definitions listed under #else
|
||||
typedef int OpenFileId;
|
||||
|
||||
class OpenFile {
|
||||
public:
|
||||
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); }
|
||||
|
||||
OpenFileId GetFileDescriptor() {
|
||||
return file;
|
||||
}
|
||||
|
||||
private:
|
||||
int file;
|
||||
|
||||
@@ -29,6 +29,7 @@ const char dbgFile = 'f'; // file system
|
||||
const char dbgAddr = 'a'; // address spaces
|
||||
const char dbgNet = 'n'; // network emulation
|
||||
const char dbgSys = 'u'; // systemcall
|
||||
const char dbgSche = 'z';
|
||||
|
||||
class Debug {
|
||||
public:
|
||||
|
||||
@@ -172,3 +172,19 @@ ConsoleOutput::PutChar(char ch)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,9 @@ class ConsoleOutput : public CallBackObj {
|
||||
void PutChar(char ch); // Write "ch" to the console display,
|
||||
// and return immediately. "callWhenDone"
|
||||
// will called when the I/O completes.
|
||||
|
||||
void PutInt(int value);
|
||||
|
||||
void CallBack(); // Invoked when next character can be put
|
||||
// out to the display.
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "copyright.h"
|
||||
#include "interrupt.h"
|
||||
#include "synchconsole.h"
|
||||
#include "main.h"
|
||||
|
||||
// String definitions for debugging messages
|
||||
@@ -359,3 +360,31 @@ Interrupt::DumpState()
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -37,35 +37,41 @@
|
||||
#include "list.h"
|
||||
#include "callback.h"
|
||||
|
||||
#include "filesys.h"
|
||||
|
||||
typedef int OpenFileId;
|
||||
|
||||
// Interrupts can be disabled (IntOff) or enabled (IntOn)
|
||||
enum IntStatus { IntOff, IntOn };
|
||||
|
||||
// Nachos can be running kernel code (SystemMode), user code (UserMode),
|
||||
// or there can be no runnable thread, because the ready list
|
||||
// is empty (IdleMode).
|
||||
enum MachineStatus {IdleMode, SystemMode, UserMode};
|
||||
enum MachineStatus { IdleMode, SystemMode, UserMode };
|
||||
|
||||
// IntType records which hardware device generated an interrupt.
|
||||
// In Nachos, we support a hardware timer device, a disk, a console
|
||||
// display and keyboard, and a network.
|
||||
enum IntType { TimerInt, DiskInt, ConsoleWriteInt, ConsoleReadInt,
|
||||
NetworkSendInt, NetworkRecvInt};
|
||||
enum IntType {
|
||||
TimerInt, DiskInt, ConsoleWriteInt, ConsoleReadInt,
|
||||
NetworkSendInt, NetworkRecvInt
|
||||
};
|
||||
|
||||
// The following class defines an interrupt that is scheduled
|
||||
// to occur in the future. The internal data structures are
|
||||
// left public to make it simpler to manipulate.
|
||||
|
||||
class PendingInterrupt {
|
||||
public:
|
||||
PendingInterrupt(CallBackObj *callOnInt, int time, IntType kind);
|
||||
// initialize an interrupt that will
|
||||
// occur in the future
|
||||
public:
|
||||
PendingInterrupt(CallBackObj* callOnInt, int time, IntType kind);
|
||||
// initialize an interrupt that will
|
||||
// occur in the future
|
||||
|
||||
CallBackObj *callOnInterrupt;// The object (in the hardware device
|
||||
// emulator) to call when the interrupt occurs
|
||||
|
||||
int when; // When the interrupt is supposed to fire
|
||||
IntType type; // for debugging
|
||||
CallBackObj* callOnInterrupt;// The object (in the hardware device
|
||||
// emulator) to call when the interrupt occurs
|
||||
|
||||
int when; // When the interrupt is supposed to fire
|
||||
IntType type; // for debugging
|
||||
};
|
||||
|
||||
// The following class defines the data structures for the simulation
|
||||
@@ -74,72 +80,76 @@ class PendingInterrupt {
|
||||
// in the future.
|
||||
|
||||
class Interrupt {
|
||||
public:
|
||||
Interrupt(); // initialize the interrupt simulation
|
||||
~Interrupt(); // de-allocate data structures
|
||||
|
||||
IntStatus SetLevel(IntStatus level);
|
||||
// Disable or enable interrupts
|
||||
// and return previous setting.
|
||||
public:
|
||||
Interrupt(); // initialize the interrupt simulation
|
||||
~Interrupt(); // de-allocate data structures
|
||||
|
||||
void Enable() { (void) SetLevel(IntOn); }
|
||||
// Enable interrupts.
|
||||
IntStatus getLevel() {return level;}
|
||||
// Return whether interrupts
|
||||
// are enabled or disabled
|
||||
|
||||
void Idle(); // The ready queue is empty, roll
|
||||
// simulated time forward until the
|
||||
// next interrupt
|
||||
IntStatus SetLevel(IntStatus level);
|
||||
// Disable or enable interrupts
|
||||
// and return previous setting.
|
||||
|
||||
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);
|
||||
int CreateFile(char *filename);
|
||||
|
||||
void YieldOnReturn(); // cause a context switch on return
|
||||
// from an interrupt handler
|
||||
void Idle(); // The ready queue is empty, roll
|
||||
// simulated time forward until the
|
||||
// next interrupt
|
||||
|
||||
MachineStatus getStatus() { return status; }
|
||||
void setStatus(MachineStatus st) { status = st; }
|
||||
// idle, kernel, user
|
||||
void Halt(); // quit and print out stats
|
||||
|
||||
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.
|
||||
// DO NOT call these directly. I should make them "private",
|
||||
// but they need to be public since they are called by the
|
||||
// hardware device simulators.
|
||||
void YieldOnReturn(); // cause a context switch on return
|
||||
// from an interrupt handler
|
||||
|
||||
void Schedule(CallBackObj *callTo, int when, IntType type);
|
||||
// Schedule an interrupt to occur
|
||||
// at time "when". This is called
|
||||
// by the hardware device simulators.
|
||||
|
||||
void OneTick(); // Advance simulated time
|
||||
MachineStatus getStatus() { return status; }
|
||||
void setStatus(MachineStatus st) { status = st; }
|
||||
// idle, kernel, user
|
||||
|
||||
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
|
||||
void DumpState(); // Print interrupt state
|
||||
|
||||
// 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
|
||||
// NOTE: the following are internal to the hardware simulation code.
|
||||
// DO NOT call these directly. I should make them "private",
|
||||
// but they need to be public since they are called by the
|
||||
// hardware device simulators.
|
||||
|
||||
void ChangeLevel(IntStatus old, // SetLevel, without advancing the
|
||||
IntStatus now); // simulated time
|
||||
void Schedule(CallBackObj* callTo, int when, IntType type);
|
||||
// Schedule an interrupt to occur
|
||||
// at time "when". This is called
|
||||
// by the hardware device simulators.
|
||||
|
||||
void OneTick(); // Advance simulated time
|
||||
|
||||
private:
|
||||
IntStatus level; // are interrupts enabled or disabled?
|
||||
SortedList<PendingInterrupt*>* pending;
|
||||
// the list of interrupts scheduled
|
||||
// to occur in the future
|
||||
//int writeFileNo; //UNIX file emulating the display
|
||||
bool inHandler; // TRUE if we are running an interrupt handler
|
||||
//bool putBusy; // Is a PrintInt operation in progress
|
||||
//If so, you cannoot do another one
|
||||
bool yieldOnReturn; // TRUE if we are to context switch
|
||||
// on return from the interrupt handler
|
||||
MachineStatus status; // idle, kernel mode, user mode
|
||||
|
||||
// these functions are internal to the interrupt simulation code
|
||||
|
||||
bool CheckIfDue(bool advanceClock);
|
||||
// Check if any interrupts are supposed
|
||||
// to occur now, and if so, do them
|
||||
|
||||
void ChangeLevel(IntStatus old, // SetLevel, without advancing the
|
||||
IntStatus now); // simulated time
|
||||
};
|
||||
|
||||
#endif // INTERRRUPT_H
|
||||
|
||||
@@ -52,7 +52,9 @@ enum ExceptionType { NoException, // Everything ok!
|
||||
// address space
|
||||
OverflowException, // Integer overflow in add or sub.
|
||||
IllegalInstrException, // Unimplemented or reserved instr.
|
||||
|
||||
|
||||
MemoryLimitException, // Insufficient memory
|
||||
|
||||
NumExceptionTypes
|
||||
};
|
||||
|
||||
|
||||
@@ -56,19 +56,19 @@ class Instruction {
|
||||
void
|
||||
Machine::Run()
|
||||
{
|
||||
Instruction *instr = new Instruction; // storage for decoded instruction
|
||||
Instruction *instr = new Instruction; // storage for decoded instruction
|
||||
|
||||
if (debug->IsEnabled('m')) {
|
||||
cout << "Starting program in thread: " << kernel->currentThread->getName();
|
||||
cout << ", at time: " << kernel->stats->totalTicks << "\n";
|
||||
}
|
||||
kernel->interrupt->setStatus(UserMode);
|
||||
for (;;) {
|
||||
OneInstruction(instr);
|
||||
kernel->interrupt->OneTick();
|
||||
if (singleStep && (runUntilTime <= kernel->stats->totalTicks))
|
||||
Debugger();
|
||||
}
|
||||
if (debug->IsEnabled('m')) {
|
||||
cout << "Starting program in thread: " << kernel->currentThread->getName();
|
||||
cout << ", at time: " << kernel->stats->totalTicks << "\n";
|
||||
}
|
||||
kernel->interrupt->setStatus(UserMode);
|
||||
for (;;) {
|
||||
OneInstruction(instr);
|
||||
kernel->interrupt->OneTick();
|
||||
if (singleStep && (runUntilTime <= kernel->stats->totalTicks))
|
||||
Debugger();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,25 +20,25 @@
|
||||
// The fields in this class are public to make it easier to update.
|
||||
|
||||
class Statistics {
|
||||
public:
|
||||
int totalTicks; // Total time running Nachos
|
||||
int idleTicks; // Time spent idle (no threads to run)
|
||||
int systemTicks; // Time spent executing system code
|
||||
int userTicks; // Time spent executing user code
|
||||
// (this is also equal to # of
|
||||
// user instructions executed)
|
||||
public:
|
||||
int totalTicks; // Total time running Nachos
|
||||
int idleTicks; // Time spent idle (no threads to run)
|
||||
int systemTicks; // Time spent executing system code
|
||||
int userTicks; // Time spent executing user code
|
||||
// (this is also equal to # of
|
||||
// user instructions executed)
|
||||
|
||||
int numDiskReads; // number of disk read requests
|
||||
int numDiskWrites; // number of disk write requests
|
||||
int numConsoleCharsRead; // number of characters read from the keyboard
|
||||
int numConsoleCharsWritten; // number of characters written to the display
|
||||
int numPageFaults; // number of virtual memory page faults
|
||||
int numPacketsSent; // number of packets sent over the network
|
||||
int numPacketsRecvd; // number of packets received over the network
|
||||
int numDiskReads; // number of disk read requests
|
||||
int numDiskWrites; // number of disk write requests
|
||||
int numConsoleCharsRead; // number of characters read from the keyboard
|
||||
int numConsoleCharsWritten; // number of characters written to the display
|
||||
int numPageFaults; // number of virtual memory page faults
|
||||
int numPacketsSent; // number of packets sent over the network
|
||||
int numPacketsRecvd; // number of packets received over the network
|
||||
|
||||
Statistics(); // initialize everything to zero
|
||||
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
|
||||
@@ -49,12 +49,15 @@ class Statistics {
|
||||
// in the kernel measured by the number of calls to enable interrupts,
|
||||
// these time constants are none too exact.
|
||||
|
||||
const int UserTick = 1; // advance for each user-level instruction
|
||||
const int SystemTick = 10; // advance each time interrupts are enabled
|
||||
const int UserTick = 1; // advance for each user-level instruction
|
||||
const int SystemTick = 10; // advance each time interrupts are enabled
|
||||
const int RotationTime = 500; // time disk takes to rotate one sector
|
||||
const int SeekTime = 500; // time disk takes to seek past one track
|
||||
const int ConsoleTime = 100; // time to read or write one character
|
||||
const int NetworkTime = 100; // time to send or receive one packet
|
||||
const int TimerTicks = 100; // (average) time between timer interrupts
|
||||
const int SeekTime = 500; // time disk takes to seek past one track
|
||||
|
||||
// MP4 MODIFIED
|
||||
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
|
||||
|
||||
@@ -107,13 +107,13 @@ LD = $(GCCDIR)ld
|
||||
|
||||
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/
|
||||
NACHOS = ../build.linux/nachos
|
||||
|
||||
ifeq ($(hosttype),unknown)
|
||||
PROGRAMS = unknownhost
|
||||
else
|
||||
# change this if you create a new test program!
|
||||
# PROGRAMS = add halt consoleIO_test1 consoleIO_test2 fileIO_test1 fileIO_test2
|
||||
PROGRAMS = halt
|
||||
PROGRAMS = mp4_consoleIO_1 mp4_consoleIO_2 mp4_consoleIO_3 mp4_consoleIO_4
|
||||
endif
|
||||
|
||||
all: $(PROGRAMS)
|
||||
@@ -121,73 +121,35 @@ all: $(PROGRAMS)
|
||||
start.o: start.S ../userprog/syscall.h
|
||||
$(CC) $(CFLAGS) $(ASFLAGS) -c start.S
|
||||
|
||||
halt.o: halt.c
|
||||
$(CC) $(CFLAGS) -c halt.c
|
||||
halt: halt.o start.o
|
||||
$(LD) $(LDFLAGS) start.o halt.o -o halt.coff
|
||||
$(COFF2NOFF) halt.coff halt
|
||||
mp4_consoleIO_1.o: mp4_consoleIO_1.c
|
||||
$(CC) $(CFLAGS) -c mp4_consoleIO_1.c
|
||||
|
||||
add.o: add.c
|
||||
$(CC) $(CFLAGS) -c add.c
|
||||
mp4_consoleIO_1: mp4_consoleIO_1.o start.o
|
||||
$(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
|
||||
$(CC) $(CFLAGS) -c shell.c
|
||||
shell: shell.o start.o
|
||||
$(LD) $(LDFLAGS) start.o shell.o -o shell.coff
|
||||
$(COFF2NOFF) shell.coff shell
|
||||
mp4_consoleIO_2.o: mp4_consoleIO_2.c
|
||||
$(CC) $(CFLAGS) -c mp4_consoleIO_2.c
|
||||
|
||||
sort.o: sort.c
|
||||
$(CC) $(CFLAGS) -c sort.c
|
||||
sort: sort.o start.o
|
||||
$(LD) $(LDFLAGS) start.o sort.o -o sort.coff
|
||||
$(COFF2NOFF) sort.coff sort
|
||||
mp4_consoleIO_2: mp4_consoleIO_2.o start.o
|
||||
$(LD) $(LDFLAGS) start.o mp4_consoleIO_2.o -o mp4_consoleIO_2.coff
|
||||
$(COFF2NOFF) mp4_consoleIO_2.coff mp4_consoleIO_2
|
||||
|
||||
segments.o: segments.c
|
||||
$(CC) $(CFLAGS) -c segments.c
|
||||
segments: segments.o start.o
|
||||
$(LD) $(LDFLAGS) start.o segments.o -o segments.coff
|
||||
$(COFF2NOFF) segments.coff segments
|
||||
mp4_consoleIO_3.o: mp4_consoleIO_3.c
|
||||
$(CC) $(CFLAGS) -c mp4_consoleIO_3.c
|
||||
|
||||
matmult.o: matmult.c
|
||||
$(CC) $(CFLAGS) -c matmult.c
|
||||
matmult: matmult.o start.o
|
||||
$(LD) $(LDFLAGS) start.o matmult.o -o matmult.coff
|
||||
$(COFF2NOFF) matmult.coff matmult
|
||||
mp4_consoleIO_3: mp4_consoleIO_3.o start.o
|
||||
$(LD) $(LDFLAGS) start.o mp4_consoleIO_3.o -o mp4_consoleIO_3.coff
|
||||
$(COFF2NOFF) mp4_consoleIO_3.coff mp4_consoleIO_3
|
||||
|
||||
consoleIO_test1.o: consoleIO_test1.c
|
||||
$(CC) $(CFLAGS) -c consoleIO_test1.c
|
||||
consoleIO_test1: consoleIO_test1.o start.o
|
||||
$(LD) $(LDFLAGS) start.o consoleIO_test1.o -o consoleIO_test1.coff
|
||||
$(COFF2NOFF) consoleIO_test1.coff consoleIO_test1
|
||||
mp4_consoleIO_4.o: mp4_consoleIO_4.c
|
||||
$(CC) $(CFLAGS) -c mp4_consoleIO_4.c
|
||||
|
||||
consoleIO_test2.o: consoleIO_test2.c
|
||||
$(CC) $(CFLAGS) -c consoleIO_test2.c
|
||||
consoleIO_test2: consoleIO_test2.o start.o
|
||||
$(LD) $(LDFLAGS) start.o consoleIO_test2.o -o consoleIO_test2.coff
|
||||
$(COFF2NOFF) consoleIO_test2.coff consoleIO_test2
|
||||
mp4_consoleIO_4: mp4_consoleIO_4.o start.o
|
||||
$(LD) $(LDFLAGS) start.o mp4_consoleIO_4.o -o mp4_consoleIO_4.coff
|
||||
$(COFF2NOFF) mp4_consoleIO_4.coff mp4_consoleIO_4
|
||||
|
||||
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:
|
||||
$(RM) -f *.o *.ii
|
||||
@@ -196,6 +158,13 @@ clean:
|
||||
distclean: clean
|
||||
$(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:
|
||||
@echo Host type could not be determined.
|
||||
@echo make is terminating.
|
||||
|
||||
BIN
code/test/add
BIN
code/test/add
Binary file not shown.
Binary file not shown.
@@ -1,12 +1,9 @@
|
||||
#include "syscall.h"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int n;
|
||||
for (n=9;n>5;n--) {
|
||||
PrintInt(n);
|
||||
}
|
||||
Halt();
|
||||
int main() {
|
||||
int n;
|
||||
for (n = 9; n > 5; n--)
|
||||
PrintInt(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,13 +1,11 @@
|
||||
#include "syscall.h"
|
||||
|
||||
int
|
||||
main()
|
||||
int main()
|
||||
{
|
||||
int n;
|
||||
for (n=15;n<=19;n++){
|
||||
|
||||
for (n=15;n<=19;n++){
|
||||
PrintInt(n);
|
||||
}
|
||||
Halt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
1
code/test/file1.test
Normal file
1
code/test/file1.test
Normal file
@@ -0,0 +1 @@
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
Binary file not shown.
Binary file not shown.
BIN
code/test/halt
BIN
code/test/halt
Binary file not shown.
10
code/test/mp4_consoleIO_1.c
Normal file
10
code/test/mp4_consoleIO_1.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "syscall.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < 4; n++) {
|
||||
PrintInt(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
9
code/test/mp4_consoleIO_2.c
Normal file
9
code/test/mp4_consoleIO_2.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "syscall.h"
|
||||
int main()
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < 5; n++) {
|
||||
PrintInt(2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
10
code/test/mp4_consoleIO_3.c
Normal file
10
code/test/mp4_consoleIO_3.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "syscall.h"
|
||||
int
|
||||
main()
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < 12; n++) {
|
||||
PrintInt(3);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
9
code/test/mp4_consoleIO_4.c
Normal file
9
code/test/mp4_consoleIO_4.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "syscall.h"
|
||||
int main()
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < 11; n++) {
|
||||
PrintInt(4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
make clean
|
||||
make distclean
|
||||
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"
|
||||
@@ -58,6 +58,14 @@ MSG:
|
||||
j $31
|
||||
.end MSG
|
||||
|
||||
.globl PrintInt
|
||||
.ent PrintInt
|
||||
PrintInt:
|
||||
addiu $2,$0,SC_PrintInt
|
||||
syscall
|
||||
j $31
|
||||
.end PrintInt
|
||||
|
||||
.globl Add
|
||||
.ent Add
|
||||
Add:
|
||||
|
||||
6
code/test/test.c
Normal file
6
code/test/test.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "syscall.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
Exit(0);
|
||||
}
|
||||
@@ -43,13 +43,15 @@ Alarm::Alarm(bool doRandom)
|
||||
// if we're currently running something (in other words, not idle).
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Alarm::CallBack()
|
||||
void
|
||||
Alarm::CallBack()
|
||||
{
|
||||
Interrupt *interrupt = kernel->interrupt;
|
||||
Interrupt* interrupt = kernel->interrupt;
|
||||
MachineStatus status = interrupt->getStatus();
|
||||
|
||||
|
||||
// Todo ----
|
||||
if (status != IdleMode) {
|
||||
interrupt->YieldOnReturn();
|
||||
// interrupt->YieldOnReturn();
|
||||
}
|
||||
// ---------
|
||||
}
|
||||
|
||||
@@ -24,60 +24,76 @@
|
||||
// for the initialization (see also comments in main.cc)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Kernel::Kernel(int argc, char **argv)
|
||||
Kernel::Kernel(int argc, char** argv)
|
||||
{
|
||||
randomSlice = FALSE;
|
||||
debugUserProg = FALSE;
|
||||
consoleIn = NULL; // default is stdin
|
||||
consoleOut = NULL; // default is stdout
|
||||
randomSlice = FALSE;
|
||||
debugUserProg = FALSE;
|
||||
consoleIn = NULL; // default is stdin
|
||||
consoleOut = NULL; // default is stdout
|
||||
#ifndef FILESYS_STUB
|
||||
formatFlag = FALSE;
|
||||
formatFlag = FALSE;
|
||||
#endif
|
||||
reliability = 1; // network reliability, default is 1.0
|
||||
hostName = 0; // machine id, also UNIX socket name
|
||||
// 0 is the default machine id
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-rs") == 0) {
|
||||
ASSERT(i + 1 < argc);
|
||||
RandomInit(atoi(argv[i + 1]));// initialize pseudo-random
|
||||
// number generator
|
||||
randomSlice = TRUE;
|
||||
i++;
|
||||
} else if (strcmp(argv[i], "-s") == 0) {
|
||||
debugUserProg = TRUE;
|
||||
} else if (strcmp(argv[i], "-e") == 0) {
|
||||
execfile[++execfileNum]= argv[++i];
|
||||
cout << execfile[execfileNum] << "\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";
|
||||
}
|
||||
reliability = 1; // network reliability, default is 1.0
|
||||
hostName = 0; // machine id, also UNIX socket name
|
||||
// 0 is the default machine id
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "-rs") == 0) {
|
||||
ASSERT(i + 1 < argc);
|
||||
RandomInit(atoi(argv[i + 1]));// initialize pseudo-random
|
||||
// number generator
|
||||
randomSlice = TRUE;
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-s") == 0) {
|
||||
debugUserProg = TRUE;
|
||||
}
|
||||
// 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
|
||||
// object to save its state.
|
||||
|
||||
|
||||
currentThread = new Thread("main", threadNum++);
|
||||
|
||||
currentThread = new Thread("main", threadNum++);
|
||||
currentThread->setStatus(RUNNING);
|
||||
|
||||
stats = new Statistics(); // collect statistics
|
||||
@@ -111,9 +127,12 @@ Kernel::Initialize()
|
||||
#else
|
||||
fileSystem = new FileSystem(formatFlag);
|
||||
#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();
|
||||
}
|
||||
|
||||
@@ -133,9 +152,13 @@ Kernel::~Kernel()
|
||||
delete synchConsoleOut;
|
||||
delete synchDisk;
|
||||
delete fileSystem;
|
||||
delete postOfficeIn;
|
||||
delete postOfficeOut;
|
||||
|
||||
|
||||
// MP4 MODIFIED
|
||||
// delete postOfficeIn;
|
||||
// delete postOfficeOut;
|
||||
|
||||
delete frameTable;
|
||||
|
||||
Exit(0);
|
||||
}
|
||||
|
||||
@@ -146,23 +169,23 @@ Kernel::~Kernel()
|
||||
|
||||
void
|
||||
Kernel::ThreadSelfTest() {
|
||||
Semaphore *semaphore;
|
||||
SynchList<int> *synchList;
|
||||
|
||||
LibSelfTest(); // test library routines
|
||||
|
||||
currentThread->SelfTest(); // test thread switching
|
||||
|
||||
// test semaphore operation
|
||||
semaphore = new Semaphore("test", 0);
|
||||
semaphore->SelfTest();
|
||||
delete semaphore;
|
||||
|
||||
// test locks, condition variables
|
||||
// using synchronized lists
|
||||
synchList = new SynchList<int>;
|
||||
synchList->SelfTest(9);
|
||||
delete synchList;
|
||||
Semaphore* semaphore;
|
||||
SynchList<int>* synchList;
|
||||
|
||||
LibSelfTest(); // test library routines
|
||||
|
||||
currentThread->SelfTest(); // test thread switching
|
||||
|
||||
// test semaphore operation
|
||||
semaphore = new Semaphore("test", 0);
|
||||
semaphore->SelfTest();
|
||||
delete semaphore;
|
||||
|
||||
// test locks, condition variables
|
||||
// using synchronized lists
|
||||
synchList = new SynchList<int>;
|
||||
synchList->SelfTest(9);
|
||||
delete synchList;
|
||||
|
||||
}
|
||||
|
||||
@@ -175,14 +198,14 @@ void
|
||||
Kernel::ConsoleTest() {
|
||||
char ch;
|
||||
|
||||
cout << "Testing the console device.\n"
|
||||
cout << "Testing the console device.\n"
|
||||
<< "Typed characters will be echoed, until ^D is typed.\n"
|
||||
<< "Note newlines are needed to flush input through UNIX.\n";
|
||||
cout.flush();
|
||||
|
||||
do {
|
||||
ch = synchConsoleIn->GetChar();
|
||||
if(ch != EOF) synchConsoleOut->PutChar(ch); // echo it!
|
||||
if (ch != EOF) synchConsoleOut->PutChar(ch); // echo it!
|
||||
} while (ch != EOF);
|
||||
|
||||
cout << "\n";
|
||||
@@ -207,28 +230,28 @@ Kernel::NetworkTest() {
|
||||
|
||||
if (hostName == 0 || hostName == 1) {
|
||||
// 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;
|
||||
MailHeader outMailHdr, inMailHdr;
|
||||
char *data = "Hello there!";
|
||||
char *ack = "Got it!";
|
||||
char* data = "Hello there!";
|
||||
char* ack = "Got it!";
|
||||
char buffer[MaxMailSize];
|
||||
|
||||
// construct packet, mail header for original message
|
||||
// To: destination machine, mailbox 0
|
||||
// From: our machine, reply to: mailbox 1
|
||||
outPktHdr.to = farHost;
|
||||
outPktHdr.to = farHost;
|
||||
outMailHdr.to = 0;
|
||||
outMailHdr.from = 1;
|
||||
outMailHdr.length = strlen(data) + 1;
|
||||
|
||||
// Send the first message
|
||||
postOfficeOut->Send(outPktHdr, outMailHdr, data);
|
||||
postOfficeOut->Send(outPktHdr, outMailHdr, data);
|
||||
|
||||
// Wait for the first message from the other machine
|
||||
postOfficeIn->Receive(0, &inPktHdr, &inMailHdr, buffer);
|
||||
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
|
||||
<< inMailHdr.from << "\n";
|
||||
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
|
||||
<< inMailHdr.from << "\n";
|
||||
cout.flush();
|
||||
|
||||
// Send acknowledgement to the other machine (using "reply to" mailbox
|
||||
@@ -236,76 +259,78 @@ Kernel::NetworkTest() {
|
||||
outPktHdr.to = inPktHdr.from;
|
||||
outMailHdr.to = inMailHdr.from;
|
||||
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
|
||||
postOfficeIn->Receive(1, &inPktHdr, &inMailHdr, buffer);
|
||||
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
|
||||
<< inMailHdr.from << "\n";
|
||||
postOfficeIn->Receive(1, &inPktHdr, &inMailHdr, buffer);
|
||||
cout << "Got: " << buffer << " : from " << inPktHdr.from << ", box "
|
||||
<< inMailHdr.from << "\n";
|
||||
cout.flush();
|
||||
}
|
||||
|
||||
// Then we're done!
|
||||
}
|
||||
|
||||
void ForkExecute(Thread *t)
|
||||
void ForkExecute(Thread* t)
|
||||
{
|
||||
if ( !t->space->Load(t->getName()) ) {
|
||||
return; // executable not found
|
||||
if (!t->space->Load(t->getName())) {
|
||||
return; // executable not found
|
||||
}
|
||||
|
||||
|
||||
t->space->Execute(t->getName());
|
||||
|
||||
}
|
||||
|
||||
void Kernel::ExecAll()
|
||||
{
|
||||
for (int i=1;i<=execfileNum;i++) {
|
||||
int a = Exec(execfile[i]);
|
||||
}
|
||||
currentThread->Finish();
|
||||
for (int i = 1;i <= execfileNum;i++) {
|
||||
int a = Exec(execfile[i], execPriority[i]);
|
||||
}
|
||||
currentThread->Finish();
|
||||
//Kernel::Exec();
|
||||
}
|
||||
|
||||
|
||||
int Kernel::Exec(char* name)
|
||||
// Todo ----
|
||||
int Kernel::Exec(char* name, int priority)
|
||||
{
|
||||
t[threadNum] = new Thread(name, threadNum);
|
||||
t[threadNum]->space = new AddrSpace();
|
||||
t[threadNum]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[threadNum]);
|
||||
threadNum++;
|
||||
t[threadNum] = new Thread(name, threadNum);
|
||||
t[threadNum]->setPriority(priority);
|
||||
// ---------
|
||||
t[threadNum]->space = new AddrSpace();
|
||||
t[threadNum]->Fork((VoidFunctionPtr)&ForkExecute, (void*)t[threadNum]);
|
||||
threadNum++;
|
||||
|
||||
return threadNum-1;
|
||||
/*
|
||||
cout << "Total threads number is " << execfileNum << endl;
|
||||
for (int n=1;n<=execfileNum;n++) {
|
||||
t[n] = new Thread(execfile[n]);
|
||||
t[n]->space = new AddrSpace();
|
||||
t[n]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[n]);
|
||||
cout << "Thread " << execfile[n] << " is executing." << endl;
|
||||
}
|
||||
cout << "debug Kernel::Run finished.\n";
|
||||
*/
|
||||
// Thread *t1 = new Thread(execfile[1]);
|
||||
// Thread *t1 = new Thread("../test/test1");
|
||||
// Thread *t2 = new Thread("../test/test2");
|
||||
return threadNum - 1;
|
||||
/*
|
||||
cout << "Total threads number is " << execfileNum << endl;
|
||||
for (int n=1;n<=execfileNum;n++) {
|
||||
t[n] = new Thread(execfile[n]);
|
||||
t[n]->space = new AddrSpace();
|
||||
t[n]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[n]);
|
||||
cout << "Thread " << execfile[n] << " is executing." << endl;
|
||||
}
|
||||
cout << "debug Kernel::Run finished.\n";
|
||||
*/
|
||||
// Thread *t1 = new Thread(execfile[1]);
|
||||
// Thread *t1 = new Thread("../test/test1");
|
||||
// Thread *t2 = new Thread("../test/test2");
|
||||
|
||||
// AddrSpace *halt = new AddrSpace();
|
||||
// t1->space = new AddrSpace();
|
||||
// t2->space = new AddrSpace();
|
||||
// AddrSpace *halt = new AddrSpace();
|
||||
// t1->space = new AddrSpace();
|
||||
// t2->space = new AddrSpace();
|
||||
|
||||
// halt->Execute("../test/halt");
|
||||
// t1->Fork((VoidFunctionPtr) &ForkExecute, (void *)t1);
|
||||
// t2->Fork((VoidFunctionPtr) &ForkExecute, (void *)t2);
|
||||
// halt->Execute("../test/halt");
|
||||
// t1->Fork((VoidFunctionPtr) &ForkExecute, (void *)t1);
|
||||
// t2->Fork((VoidFunctionPtr) &ForkExecute, (void *)t2);
|
||||
|
||||
// currentThread->Finish();
|
||||
// Kernel::Run();
|
||||
// cout << "after ThreadedKernel:Run();" << endl; // unreachable
|
||||
// currentThread->Finish();
|
||||
// Kernel::Run();
|
||||
// cout << "after ThreadedKernel:Run();" << endl; // unreachable
|
||||
}
|
||||
|
||||
int Kernel::CreateFile(char *filename)
|
||||
int Kernel::CreateFile(char* filename)
|
||||
{
|
||||
return fileSystem->Create(filename);
|
||||
return fileSystem->Create(filename);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,29 +25,34 @@ class SynchConsoleInput;
|
||||
class SynchConsoleOutput;
|
||||
class SynchDisk;
|
||||
|
||||
|
||||
// Todo ----
|
||||
// ---------
|
||||
|
||||
class Kernel {
|
||||
public:
|
||||
Kernel(int argc, char **argv);
|
||||
// Interpret command line arguments
|
||||
// Interpret command line arguments
|
||||
~Kernel(); // deallocate the kernel
|
||||
|
||||
|
||||
void Initialize(); // initialize the kernel -- separated
|
||||
// from constructor because
|
||||
// refers to "kernel" as a global
|
||||
void ExecAll();
|
||||
int Exec(char* name);
|
||||
// from constructor because
|
||||
// refers to "kernel" as a global
|
||||
void ExecAll();
|
||||
|
||||
// Todo ----
|
||||
int Exec(char* name, int priority);
|
||||
// ---------
|
||||
|
||||
void ThreadSelfTest(); // self test of threads and synchronization
|
||||
|
||||
|
||||
void ConsoleTest(); // interactive console self test
|
||||
void NetworkTest(); // interactive 2-machine network test
|
||||
Thread* getThread(int threadID){return t[threadID];}
|
||||
|
||||
int CreateFile(char* filename); // fileSystem call
|
||||
Thread* getThread(int threadID){return t[threadID];}
|
||||
|
||||
// These are public for notational convenience; really,
|
||||
// they're global variables used everywhere.
|
||||
int CreateFile(char* filename); // fileSystem call
|
||||
|
||||
// These are public for notational convenience; really,
|
||||
// they're global variables used everywhere.
|
||||
|
||||
Thread *currentThread; // the thread holding the CPU
|
||||
Scheduler *scheduler; // the ready list
|
||||
@@ -61,15 +66,20 @@ class Kernel {
|
||||
FileSystem *fileSystem;
|
||||
PostOfficeInput *postOfficeIn;
|
||||
PostOfficeOutput *postOfficeOut;
|
||||
|
||||
FrameTable *frameTable;
|
||||
int hostName; // machine identifier
|
||||
|
||||
private:
|
||||
|
||||
Thread* t[10];
|
||||
char* execfile[10];
|
||||
int execfileNum;
|
||||
int threadNum;
|
||||
Thread* t[10];
|
||||
|
||||
// Todo ----
|
||||
char* execfile[10];
|
||||
int execPriority[10];
|
||||
// ---------
|
||||
|
||||
int execfileNum;
|
||||
int threadNum;
|
||||
bool randomSlice; // enable pseudo-random time slicing
|
||||
bool debugUserProg; // single step user program
|
||||
double reliability; // likelihood messages are dropped
|
||||
@@ -82,5 +92,3 @@ class Kernel {
|
||||
|
||||
|
||||
#endif // KERNEL_H
|
||||
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
#include "sysdep.h"
|
||||
|
||||
// global variables
|
||||
Kernel *kernel;
|
||||
Debug *debug;
|
||||
Kernel* kernel;
|
||||
Debug* debug;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -56,11 +56,11 @@ Debug *debug;
|
||||
// Delete kernel data structures; called when user hits "ctl-C".
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
Cleanup(int x)
|
||||
{
|
||||
static void
|
||||
Cleanup(int x)
|
||||
{
|
||||
cerr << "\nCleaning up after signal " << x << "\n";
|
||||
delete kernel;
|
||||
delete kernel;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
@@ -78,42 +78,42 @@ static const int TransferSize = 128;
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
Copy(char *from, char *to)
|
||||
Copy(char* from, char* to)
|
||||
{
|
||||
int fd;
|
||||
OpenFile* openFile;
|
||||
int amountRead, fileLength;
|
||||
char *buffer;
|
||||
char* buffer;
|
||||
|
||||
// Open UNIX file
|
||||
if ((fd = OpenForReadWrite(from,FALSE)) < 0) {
|
||||
// Open UNIX file
|
||||
if ((fd = OpenForReadWrite(from, FALSE)) < 0) {
|
||||
printf("Copy: couldn't open input file %s\n", from);
|
||||
return;
|
||||
}
|
||||
|
||||
// Figure out length of UNIX file
|
||||
Lseek(fd, 0, 2);
|
||||
// Figure out length of UNIX file
|
||||
Lseek(fd, 0, 2);
|
||||
fileLength = Tell(fd);
|
||||
Lseek(fd, 0, 0);
|
||||
|
||||
// Create a Nachos file of the same length
|
||||
DEBUG('f', "Copying file " << from << " of size " << fileLength << " to file " << to);
|
||||
// Create a Nachos file of the same length
|
||||
DEBUG('f', "Copying file " << from << " of size " << fileLength << " to file " << to);
|
||||
if (!kernel->fileSystem->Create(to, fileLength)) { // Create Nachos file
|
||||
printf("Copy: couldn't create output file %s\n", to);
|
||||
Close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
openFile = kernel->fileSystem->Open(to);
|
||||
ASSERT(openFile != NULL);
|
||||
|
||||
// Copy the data in TransferSize chunks
|
||||
buffer = new char[TransferSize];
|
||||
while ((amountRead=ReadPartial(fd, buffer, sizeof(char)*TransferSize)) > 0)
|
||||
openFile->Write(buffer, amountRead);
|
||||
delete [] buffer;
|
||||
|
||||
// Close the UNIX and the Nachos files
|
||||
// Copy the data in TransferSize chunks
|
||||
buffer = new char[TransferSize];
|
||||
while ((amountRead = ReadPartial(fd, buffer, sizeof(char) * TransferSize)) > 0)
|
||||
openFile->Write(buffer, amountRead);
|
||||
delete[] buffer;
|
||||
|
||||
// Close the UNIX and the Nachos files
|
||||
delete openFile;
|
||||
Close(fd);
|
||||
}
|
||||
@@ -126,22 +126,22 @@ Copy(char *from, char *to)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Print(char *name)
|
||||
Print(char* name)
|
||||
{
|
||||
OpenFile *openFile;
|
||||
OpenFile* openFile;
|
||||
int i, amountRead;
|
||||
char *buffer;
|
||||
char* buffer;
|
||||
|
||||
if ((openFile = kernel->fileSystem->Open(name)) == NULL) {
|
||||
printf("Print: unable to open file %s\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
buffer = new char[TransferSize];
|
||||
while ((amountRead = openFile->Read(buffer, TransferSize)) > 0)
|
||||
for (i = 0; i < amountRead; i++)
|
||||
printf("%c", buffer[i]);
|
||||
delete [] buffer;
|
||||
delete[] buffer;
|
||||
|
||||
delete openFile; // close the Nachos file
|
||||
return;
|
||||
@@ -164,19 +164,19 @@ Print(char *name)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
int i;
|
||||
char *debugArg = "";
|
||||
char *userProgName = NULL; // default is not to execute a user prog
|
||||
char* debugArg = "";
|
||||
char* userProgName = NULL; // default is not to execute a user prog
|
||||
bool threadTestFlag = false;
|
||||
bool consoleTestFlag = false;
|
||||
bool networkTestFlag = false;
|
||||
#ifndef FILESYS_STUB
|
||||
char *copyUnixFileName = NULL; // UNIX file to be copied into Nachos
|
||||
char *copyNachosFileName = NULL; // name of copied file in Nachos
|
||||
char *printFileName = NULL;
|
||||
char *removeFileName = NULL;
|
||||
char* copyUnixFileName = NULL; // UNIX file to be copied into Nachos
|
||||
char* copyNachosFileName = NULL; // name of copied file in Nachos
|
||||
char* printFileName = NULL;
|
||||
char* removeFileName = NULL;
|
||||
bool dirListFlag = false;
|
||||
bool dumpFlag = false;
|
||||
#endif //FILESYS_STUB
|
||||
@@ -186,65 +186,65 @@ main(int argc, char **argv)
|
||||
// the Kernel constructor
|
||||
for (i = 1; i < argc; i++) {
|
||||
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];
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-z") == 0) {
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-z") == 0) {
|
||||
cout << copyright << "\n";
|
||||
}
|
||||
else if (strcmp(argv[i], "-x") == 0) {
|
||||
ASSERT(i + 1 < argc);
|
||||
userProgName = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-K") == 0) {
|
||||
threadTestFlag = TRUE;
|
||||
}
|
||||
else if (strcmp(argv[i], "-C") == 0) {
|
||||
consoleTestFlag = TRUE;
|
||||
}
|
||||
else if (strcmp(argv[i], "-N") == 0) {
|
||||
networkTestFlag = TRUE;
|
||||
}
|
||||
}
|
||||
else if (strcmp(argv[i], "-x") == 0) {
|
||||
ASSERT(i + 1 < argc);
|
||||
userProgName = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-K") == 0) {
|
||||
threadTestFlag = TRUE;
|
||||
}
|
||||
else if (strcmp(argv[i], "-C") == 0) {
|
||||
consoleTestFlag = TRUE;
|
||||
}
|
||||
else if (strcmp(argv[i], "-N") == 0) {
|
||||
networkTestFlag = TRUE;
|
||||
}
|
||||
#ifndef FILESYS_STUB
|
||||
else if (strcmp(argv[i], "-cp") == 0) {
|
||||
ASSERT(i + 2 < argc);
|
||||
copyUnixFileName = argv[i + 1];
|
||||
copyNachosFileName = argv[i + 2];
|
||||
i += 2;
|
||||
}
|
||||
else if (strcmp(argv[i], "-p") == 0) {
|
||||
ASSERT(i + 1 < argc);
|
||||
printFileName = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-r") == 0) {
|
||||
ASSERT(i + 1 < argc);
|
||||
removeFileName = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-l") == 0) {
|
||||
dirListFlag = true;
|
||||
}
|
||||
else if (strcmp(argv[i], "-D") == 0) {
|
||||
dumpFlag = true;
|
||||
}
|
||||
else if (strcmp(argv[i], "-cp") == 0) {
|
||||
ASSERT(i + 2 < argc);
|
||||
copyUnixFileName = argv[i + 1];
|
||||
copyNachosFileName = argv[i + 2];
|
||||
i += 2;
|
||||
}
|
||||
else if (strcmp(argv[i], "-p") == 0) {
|
||||
ASSERT(i + 1 < argc);
|
||||
printFileName = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-r") == 0) {
|
||||
ASSERT(i + 1 < argc);
|
||||
removeFileName = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
else if (strcmp(argv[i], "-l") == 0) {
|
||||
dirListFlag = true;
|
||||
}
|
||||
else if (strcmp(argv[i], "-D") == 0) {
|
||||
dumpFlag = true;
|
||||
}
|
||||
#endif //FILESYS_STUB
|
||||
else if (strcmp(argv[i], "-u") == 0) {
|
||||
else if (strcmp(argv[i], "-u") == 0) {
|
||||
cout << "Partial usage: nachos [-z -d debugFlags]\n";
|
||||
cout << "Partial usage: nachos [-x programName]\n";
|
||||
cout << "Partial usage: nachos [-K] [-C] [-N]\n";
|
||||
cout << "Partial usage: nachos [-K] [-C] [-N]\n";
|
||||
#ifndef FILESYS_STUB
|
||||
cout << "Partial usage: nachos [-cp UnixFile NachosFile]\n";
|
||||
cout << "Partial usage: nachos [-p fileName] [-r fileName]\n";
|
||||
cout << "Partial usage: nachos [-l] [-D]\n";
|
||||
#endif //FILESYS_STUB
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
debug = new Debug(debugArg);
|
||||
|
||||
|
||||
DEBUG(dbgThread, "Entering main");
|
||||
|
||||
kernel = new Kernel(argc, argv);
|
||||
@@ -256,42 +256,42 @@ main(int argc, char **argv)
|
||||
// at this point, the kernel is ready to do something
|
||||
// run some tests, if requested
|
||||
if (threadTestFlag) {
|
||||
kernel->ThreadSelfTest(); // test threads and synchronization
|
||||
kernel->ThreadSelfTest(); // test threads and synchronization
|
||||
}
|
||||
if (consoleTestFlag) {
|
||||
kernel->ConsoleTest(); // interactive test of the synchronized console
|
||||
kernel->ConsoleTest(); // interactive test of the synchronized console
|
||||
}
|
||||
if (networkTestFlag) {
|
||||
kernel->NetworkTest(); // two-machine test of the network
|
||||
kernel->NetworkTest(); // two-machine test of the network
|
||||
}
|
||||
|
||||
#ifndef FILESYS_STUB
|
||||
if (removeFileName != NULL) {
|
||||
kernel->fileSystem->Remove(removeFileName);
|
||||
kernel->fileSystem->Remove(removeFileName);
|
||||
}
|
||||
if (copyUnixFileName != NULL && copyNachosFileName != NULL) {
|
||||
Copy(copyUnixFileName,copyNachosFileName);
|
||||
Copy(copyUnixFileName, copyNachosFileName);
|
||||
}
|
||||
if (dumpFlag) {
|
||||
kernel->fileSystem->Print();
|
||||
kernel->fileSystem->Print();
|
||||
}
|
||||
if (dirListFlag) {
|
||||
kernel->fileSystem->List();
|
||||
kernel->fileSystem->List();
|
||||
}
|
||||
if (printFileName != NULL) {
|
||||
Print(printFileName);
|
||||
Print(printFileName);
|
||||
}
|
||||
#endif // FILESYS_STUB
|
||||
|
||||
// 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.
|
||||
// Calling "return" would terminate the program.
|
||||
// Instead, call Halt, which will first clean up, then
|
||||
// terminate.
|
||||
// kernel->interrupt->Halt();
|
||||
|
||||
|
||||
ASSERTNOTREACHED();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "debug.h"
|
||||
#include "scheduler.h"
|
||||
#include "main.h"
|
||||
#include <functional>
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Scheduler::Scheduler
|
||||
@@ -29,11 +30,22 @@
|
||||
// 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()
|
||||
{
|
||||
readyList = new List<Thread *>;
|
||||
toBeDestroyed = NULL;
|
||||
}
|
||||
{
|
||||
readyList = new SortedList(cmp);
|
||||
// ---------
|
||||
|
||||
toBeDestroyed = NULL;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Scheduler::~Scheduler
|
||||
@@ -41,9 +53,9 @@ Scheduler::Scheduler()
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
Scheduler::~Scheduler()
|
||||
{
|
||||
delete readyList;
|
||||
}
|
||||
{
|
||||
delete readyList;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Scheduler::ReadyToRun
|
||||
@@ -54,13 +66,15 @@ Scheduler::~Scheduler()
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Scheduler::ReadyToRun (Thread *thread)
|
||||
Scheduler::ReadyToRun(Thread* thread)
|
||||
{
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName());
|
||||
//cout << "Putting thread on ready list: " << thread->getName() << endl ;
|
||||
thread->setStatus(READY);
|
||||
readyList->Append(thread);
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
DEBUG(dbgThread, "Putting thread on ready list: " << thread->getName());
|
||||
//cout << "Putting thread on ready list: " << thread->getName() << endl ;
|
||||
thread->setStatus(READY);
|
||||
|
||||
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 *
|
||||
Scheduler::FindNextToRun ()
|
||||
Thread*
|
||||
Scheduler::FindNextToRun()
|
||||
{
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
|
||||
if (readyList->IsEmpty()) {
|
||||
return NULL;
|
||||
} else {
|
||||
return readyList->RemoveFront();
|
||||
}
|
||||
if (readyList->IsEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
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
|
||||
Scheduler::Run (Thread *nextThread, bool finishing)
|
||||
Scheduler::Run(Thread* nextThread, bool finishing)
|
||||
{
|
||||
Thread *oldThread = kernel->currentThread;
|
||||
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
Thread* oldThread = kernel->currentThread;
|
||||
|
||||
if (finishing) { // mark that we need to delete current thread
|
||||
ASSERT(toBeDestroyed == NULL);
|
||||
toBeDestroyed = oldThread;
|
||||
}
|
||||
|
||||
if (oldThread->space != NULL) { // if this thread is a user program,
|
||||
oldThread->SaveUserState(); // save the user's CPU registers
|
||||
oldThread->space->SaveState();
|
||||
}
|
||||
|
||||
oldThread->CheckOverflow(); // check if the old thread
|
||||
// had an undetected stack overflow
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
|
||||
kernel->currentThread = nextThread; // switch to the next thread
|
||||
nextThread->setStatus(RUNNING); // nextThread is now running
|
||||
|
||||
DEBUG(dbgThread, "Switching from: " << oldThread->getName() << " to: " << nextThread->getName());
|
||||
|
||||
// This is a machine-dependent assembly language routine defined
|
||||
// in switch.s. You may have to think
|
||||
// a bit to figure out what happens after this, both from the point
|
||||
// of view of the thread and from the perspective of the "outside world".
|
||||
if (finishing) { // mark that we need to delete current thread
|
||||
ASSERT(toBeDestroyed == NULL);
|
||||
toBeDestroyed = oldThread;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// interrupts are off when we return from switch!
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
oldThread->CheckOverflow(); // check if the old thread
|
||||
// had an undetected stack overflow
|
||||
|
||||
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
|
||||
// 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();
|
||||
}
|
||||
DEBUG(dbgThread, "Switching from: " << oldThread->getName() << " to: " << nextThread->getName());
|
||||
DEBUG(dbgSche, "[C] Tick [" << kernel->stats->totalTicks << "]: Process [" << nextThread->getName() << "] is now selected for execution, thread [" << oldThread->getName() << "] is replaced.");
|
||||
|
||||
// This is a machine-dependent assembly language routine defined
|
||||
// in switch.s. You may have to think
|
||||
// a bit to figure out what happens after this, both from the point
|
||||
// of view of the thread and from the perspective of the "outside world".
|
||||
|
||||
SWITCH(oldThread, nextThread);
|
||||
|
||||
// we're back, running oldThread
|
||||
|
||||
// interrupts are off when we return from switch!
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
|
||||
DEBUG(dbgThread, "Now in thread: " << oldThread->getName());
|
||||
|
||||
CheckToBeDestroyed(); // check if thread we were running
|
||||
// before this one has finished
|
||||
// and needs to be cleaned up
|
||||
|
||||
if (oldThread->space != NULL) { // if there is an address space
|
||||
oldThread->RestoreUserState(); // to restore, do it.
|
||||
oldThread->space->RestoreState();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -160,12 +177,12 @@ Scheduler::Run (Thread *nextThread, bool finishing)
|
||||
void
|
||||
Scheduler::CheckToBeDestroyed()
|
||||
{
|
||||
if (toBeDestroyed != NULL) {
|
||||
delete toBeDestroyed;
|
||||
toBeDestroyed = NULL;
|
||||
}
|
||||
if (toBeDestroyed != NULL) {
|
||||
delete toBeDestroyed;
|
||||
toBeDestroyed = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Scheduler::Print
|
||||
// Print the scheduler state -- in other words, the contents of
|
||||
@@ -174,6 +191,6 @@ Scheduler::CheckToBeDestroyed()
|
||||
void
|
||||
Scheduler::Print()
|
||||
{
|
||||
cout << "Ready list contents:\n";
|
||||
readyList->Apply(ThreadPrint);
|
||||
cout << "Ready list contents:\n";
|
||||
readyList->Apply(ThreadPrint);
|
||||
}
|
||||
|
||||
@@ -23,22 +23,25 @@ class Scheduler {
|
||||
~Scheduler(); // De-allocate ready list
|
||||
|
||||
void ReadyToRun(Thread* thread);
|
||||
// Thread can be dispatched.
|
||||
// Thread can be dispatched.
|
||||
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);
|
||||
// Cause nextThread to start running
|
||||
// Cause nextThread to start running
|
||||
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
|
||||
|
||||
|
||||
// SelfTest for scheduler is implemented in class Thread
|
||||
|
||||
private:
|
||||
List<Thread *> *readyList; // queue of threads that are ready to run,
|
||||
// but not running
|
||||
// Todo ----
|
||||
SortedList<Thread *> *readyList; // queue of threads that are ready to run,
|
||||
// but not running
|
||||
// ---------
|
||||
|
||||
Thread *toBeDestroyed; // finishing thread to be destroyed
|
||||
// by the next thread that runs
|
||||
// by the next thread that runs
|
||||
};
|
||||
|
||||
#endif // SCHEDULER_H
|
||||
|
||||
@@ -48,7 +48,7 @@ Semaphore::Semaphore(char* debugName, int initialValue)
|
||||
{
|
||||
name = debugName;
|
||||
value = initialValue;
|
||||
queue = new List<Thread *>;
|
||||
queue = new List<Thread*>;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -75,20 +75,20 @@ Semaphore::~Semaphore()
|
||||
void
|
||||
Semaphore::P()
|
||||
{
|
||||
Interrupt *interrupt = kernel->interrupt;
|
||||
Thread *currentThread = kernel->currentThread;
|
||||
|
||||
Interrupt* interrupt = kernel->interrupt;
|
||||
Thread* currentThread = kernel->currentThread;
|
||||
|
||||
// disable interrupts
|
||||
IntStatus oldLevel = interrupt->SetLevel(IntOff);
|
||||
|
||||
IntStatus oldLevel = interrupt->SetLevel(IntOff);
|
||||
|
||||
while (value == 0) { // semaphore not available
|
||||
queue->Append(currentThread); // so go to sleep
|
||||
currentThread->Sleep(FALSE);
|
||||
}
|
||||
queue->Append(currentThread); // so go to sleep
|
||||
currentThread->Sleep(FALSE);
|
||||
}
|
||||
value--; // semaphore available, consume its value
|
||||
|
||||
|
||||
// re-enable interrupts
|
||||
(void) interrupt->SetLevel(oldLevel);
|
||||
(void)interrupt->SetLevel(oldLevel);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -102,18 +102,18 @@ Semaphore::P()
|
||||
void
|
||||
Semaphore::V()
|
||||
{
|
||||
Interrupt *interrupt = kernel->interrupt;
|
||||
|
||||
Interrupt* interrupt = kernel->interrupt;
|
||||
|
||||
// disable interrupts
|
||||
IntStatus oldLevel = interrupt->SetLevel(IntOff);
|
||||
|
||||
IntStatus oldLevel = interrupt->SetLevel(IntOff);
|
||||
|
||||
if (!queue->IsEmpty()) { // make thread ready.
|
||||
kernel->scheduler->ReadyToRun(queue->RemoveFront());
|
||||
kernel->scheduler->ReadyToRun(queue->RemoveFront());
|
||||
}
|
||||
value++;
|
||||
|
||||
|
||||
// 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.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static Semaphore *ping;
|
||||
static Semaphore* ping;
|
||||
static void
|
||||
SelfTestHelper (Semaphore *pong)
|
||||
SelfTestHelper(Semaphore* pong)
|
||||
{
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ping->P();
|
||||
pong->V();
|
||||
pong->V();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Semaphore::SelfTest()
|
||||
{
|
||||
Thread *helper = new Thread("ping", 1);
|
||||
Thread* helper = new Thread("ping", 1);
|
||||
|
||||
ASSERT(value == 0); // otherwise test won't work!
|
||||
ping = new Semaphore("ping", 0);
|
||||
helper->Fork((VoidFunctionPtr) SelfTestHelper, this);
|
||||
helper->Fork((VoidFunctionPtr)SelfTestHelper, this);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ping->V();
|
||||
this->P();
|
||||
this->P();
|
||||
}
|
||||
delete ping;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ void Lock::Release()
|
||||
Condition::Condition(char* 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
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void Condition::Wait(Lock* conditionLock)
|
||||
void Condition::Wait(Lock* conditionLock)
|
||||
{
|
||||
Semaphore *waiter;
|
||||
|
||||
ASSERT(conditionLock->IsHeldByCurrentThread());
|
||||
Semaphore* waiter;
|
||||
|
||||
waiter = new Semaphore("condition", 0);
|
||||
waitQueue->Append(waiter);
|
||||
conditionLock->Release();
|
||||
waiter->P();
|
||||
conditionLock->Acquire();
|
||||
delete waiter;
|
||||
ASSERT(conditionLock->IsHeldByCurrentThread());
|
||||
|
||||
waiter = new Semaphore("condition", 0);
|
||||
waitQueue->Append(waiter);
|
||||
conditionLock->Release();
|
||||
waiter->P();
|
||||
conditionLock->Acquire();
|
||||
delete waiter;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -272,13 +272,13 @@ void Condition::Wait(Lock* conditionLock)
|
||||
|
||||
void Condition::Signal(Lock* conditionLock)
|
||||
{
|
||||
Semaphore *waiter;
|
||||
|
||||
Semaphore* waiter;
|
||||
|
||||
ASSERT(conditionLock->IsHeldByCurrentThread());
|
||||
|
||||
|
||||
if (!waitQueue->IsEmpty()) {
|
||||
waiter = waitQueue->RemoveFront();
|
||||
waiter->V();
|
||||
waiter->V();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ void Condition::Signal(Lock* conditionLock)
|
||||
// "conditionLock" -- lock protecting the use of this condition
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void Condition::Broadcast(Lock* conditionLock)
|
||||
void Condition::Broadcast(Lock* conditionLock)
|
||||
{
|
||||
while (!waitQueue->IsEmpty()) {
|
||||
Signal(conditionLock);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// thread.cc
|
||||
// thread.cc
|
||||
// Routines to manage threads. These are the main operations:
|
||||
//
|
||||
// Fork -- create a thread to run a procedure concurrently
|
||||
@@ -9,11 +9,11 @@
|
||||
// Finish -- called when the forked procedure finishes, to clean up
|
||||
// Yield -- relinquish control over the CPU to another ready thread
|
||||
// Sleep -- relinquish control over the CPU, but thread is now blocked.
|
||||
// In other words, it will not run again, until explicitly
|
||||
// In other words, it will not run again, until explicitly
|
||||
// put back on the ready queue.
|
||||
//
|
||||
// Copyright (c) 1992-1996 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#include "copyright.h"
|
||||
@@ -33,17 +33,18 @@ const int STACK_FENCEPOST = 0xdedbeef;
|
||||
// "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;
|
||||
stackTop = NULL;
|
||||
stack = NULL;
|
||||
status = JUST_CREATED;
|
||||
for (int i = 0; i < MachineStateSize; i++) {
|
||||
machineState[i] = NULL; // not strictly necessary, since
|
||||
// new thread ignores contents
|
||||
// of machine registers
|
||||
for (int i = 0; i < MachineStateSize; i++)
|
||||
{
|
||||
machineState[i] = NULL; // not strictly necessary, since
|
||||
// new thread ignores contents
|
||||
// of machine registers
|
||||
}
|
||||
space = NULL;
|
||||
}
|
||||
@@ -65,12 +66,12 @@ Thread::~Thread()
|
||||
DEBUG(dbgThread, "Deleting thread: " << name);
|
||||
ASSERT(this != kernel->currentThread);
|
||||
if (stack != NULL)
|
||||
DeallocBoundedArray((char *) stack, StackSize * sizeof(int));
|
||||
DeallocBoundedArray((char *)stack, StackSize * sizeof(int));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Thread::Fork
|
||||
// Invoke (*func)(arg), allowing caller and callee to execute
|
||||
// Invoke (*func)(arg), allowing caller and callee to execute
|
||||
// concurrently.
|
||||
//
|
||||
// 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
|
||||
// cause it to run the procedure
|
||||
// 3. Put the thread on the ready queue
|
||||
//
|
||||
//
|
||||
// "func" is the procedure to run concurrently.
|
||||
// "arg" is a single argument to be passed to the procedure.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Thread::Fork(VoidFunctionPtr func, void *arg)
|
||||
void Thread::Fork(VoidFunctionPtr func, void *arg)
|
||||
{
|
||||
Interrupt *interrupt = kernel->interrupt;
|
||||
Scheduler *scheduler = kernel->scheduler;
|
||||
IntStatus oldLevel;
|
||||
|
||||
DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int) func << " " << arg);
|
||||
|
||||
DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int)func << " " << arg);
|
||||
StackAllocate(func, arg);
|
||||
|
||||
oldLevel = interrupt->SetLevel(IntOff);
|
||||
scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts
|
||||
// are disabled!
|
||||
(void) interrupt->SetLevel(oldLevel);
|
||||
}
|
||||
scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts
|
||||
// are disabled!
|
||||
(void)interrupt->SetLevel(oldLevel);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Thread::CheckOverflow
|
||||
@@ -119,16 +119,16 @@ Thread::Fork(VoidFunctionPtr func, void *arg)
|
||||
// Don't do this: void foo() { int bigArray[10000]; ... }
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Thread::CheckOverflow()
|
||||
void Thread::CheckOverflow()
|
||||
{
|
||||
if (stack != NULL) {
|
||||
#ifdef HPUX // Stacks grow upward on the Snakes
|
||||
ASSERT(stack[StackSize - 1] == STACK_FENCEPOST);
|
||||
if (stack != NULL)
|
||||
{
|
||||
#ifdef HPUX // Stacks grow upward on the Snakes
|
||||
ASSERT(stack[StackSize - 1] == STACK_FENCEPOST);
|
||||
#else
|
||||
ASSERT(*stack == STACK_FENCEPOST);
|
||||
ASSERT(*stack == STACK_FENCEPOST);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -137,28 +137,27 @@ Thread::CheckOverflow()
|
||||
// executing the forked procedure.
|
||||
//
|
||||
// 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())
|
||||
// 2. enable interrupts (so we can get time-sliced)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Thread::Begin ()
|
||||
void Thread::Begin()
|
||||
{
|
||||
ASSERT(this == kernel->currentThread);
|
||||
DEBUG(dbgThread, "Beginning thread: " << name);
|
||||
|
||||
|
||||
kernel->scheduler->CheckToBeDestroyed();
|
||||
kernel->interrupt->Enable();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Thread::Finish
|
||||
// Called by ThreadRoot when a thread is done executing the
|
||||
// Called by ThreadRoot when a thread is done executing the
|
||||
// forked procedure.
|
||||
//
|
||||
// NOTE: we can't immediately de-allocate the thread data structure
|
||||
// or the execution stack, because we're still running in the thread
|
||||
// NOTE: we can't immediately de-allocate the thread data structure
|
||||
// or the execution stack, because we're still running in the thread
|
||||
// and we're still on the stack! Instead, we tell the scheduler
|
||||
// to call the destructor, once it is running in the context of a different thread.
|
||||
//
|
||||
@@ -167,18 +166,16 @@ Thread::Begin ()
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
//
|
||||
void
|
||||
Thread::Finish ()
|
||||
void Thread::Finish()
|
||||
{
|
||||
(void) kernel->interrupt->SetLevel(IntOff);
|
||||
(void)kernel->interrupt->SetLevel(IntOff);
|
||||
ASSERT(this == kernel->currentThread);
|
||||
|
||||
|
||||
DEBUG(dbgThread, "Finishing thread: " << name);
|
||||
Sleep(TRUE); // invokes SWITCH
|
||||
Sleep(TRUE); // invokes SWITCH
|
||||
// not reached
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Thread::Yield
|
||||
// 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
|
||||
// on the front of the ready list, and switching to it, can be done
|
||||
// atomically. On return, we re-set the interrupt level to its
|
||||
// original state, in case we are called with interrupts disabled.
|
||||
// original state, in case we are called with interrupts disabled.
|
||||
//
|
||||
// Similar to Thread::Sleep(), but a little different.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Thread::Yield ()
|
||||
void Thread::Yield()
|
||||
{
|
||||
Thread *nextThread;
|
||||
IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff);
|
||||
|
||||
|
||||
ASSERT(this == kernel->currentThread);
|
||||
|
||||
|
||||
DEBUG(dbgThread, "Yielding thread: " << name);
|
||||
|
||||
|
||||
nextThread = kernel->scheduler->FindNextToRun();
|
||||
if (nextThread != NULL) {
|
||||
kernel->scheduler->ReadyToRun(this);
|
||||
kernel->scheduler->Run(nextThread, FALSE);
|
||||
if (nextThread != NULL)
|
||||
{
|
||||
kernel->scheduler->ReadyToRun(this);
|
||||
kernel->scheduler->Run(nextThread, FALSE);
|
||||
}
|
||||
(void) kernel->interrupt->SetLevel(oldLevel);
|
||||
(void)kernel->interrupt->SetLevel(oldLevel);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Thread::Sleep
|
||||
// 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,
|
||||
// eventually some thread will wake this thread up, and put it
|
||||
// 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
|
||||
// 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
|
||||
// off the ready list, and switching to it.
|
||||
//----------------------------------------------------------------------
|
||||
void
|
||||
Thread::Sleep (bool finishing)
|
||||
void Thread::Sleep(bool finishing)
|
||||
{
|
||||
Thread *nextThread;
|
||||
|
||||
|
||||
ASSERT(this == kernel->currentThread);
|
||||
ASSERT(kernel->interrupt->getLevel() == IntOff);
|
||||
|
||||
|
||||
DEBUG(dbgThread, "Sleeping thread: " << name);
|
||||
|
||||
status = BLOCKED;
|
||||
//cout << "debug Thread::Sleep " << name << "wait for Idle\n";
|
||||
while ((nextThread = kernel->scheduler->FindNextToRun()) == NULL) {
|
||||
kernel->interrupt->Idle(); // no one to run, wait for an interrupt
|
||||
}
|
||||
// cout << "debug Thread::Sleep " << name << "wait for Idle\n";
|
||||
while ((nextThread = kernel->scheduler->FindNextToRun()) == NULL)
|
||||
{
|
||||
kernel->interrupt->Idle(); // no one to run, wait for an interrupt
|
||||
}
|
||||
// returns when it's time for us to run
|
||||
kernel->scheduler->Run(nextThread, finishing);
|
||||
kernel->scheduler->Run(nextThread, finishing);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// ThreadBegin, ThreadFinish, ThreadPrint
|
||||
// Dummy functions because C++ does not (easily) allow pointers to member
|
||||
// functions. So we create a dummy C function
|
||||
// (which we can pass a pointer to), that then simply calls the
|
||||
// (which we can pass a pointer to), that then simply calls the
|
||||
// member function.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void ThreadFinish() { kernel->currentThread->Finish(); }
|
||||
static void ThreadFinish() { kernel->currentThread->Finish(); }
|
||||
static void ThreadBegin() { kernel->currentThread->Begin(); }
|
||||
void ThreadPrint(Thread *t) { t->Print(); }
|
||||
|
||||
@@ -277,13 +274,16 @@ void ThreadPrint(Thread *t) { t->Print(); }
|
||||
static void *
|
||||
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
|
||||
funcPtr -= 2; // Get rid of the L bit
|
||||
funcPtr -= 2; // Get rid of the L bit
|
||||
return (*(void **)funcPtr);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// L-field not set.
|
||||
return plabel;
|
||||
}
|
||||
@@ -302,50 +302,48 @@ PLabelToAddr(void *plabel)
|
||||
// "arg" is the parameter to be passed to the procedure
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Thread::StackAllocate (VoidFunctionPtr func, void *arg)
|
||||
void Thread::StackAllocate(VoidFunctionPtr func, void *arg)
|
||||
{
|
||||
stack = (int *) AllocBoundedArray(StackSize * sizeof(int));
|
||||
stack = (int *)AllocBoundedArray(StackSize * sizeof(int));
|
||||
|
||||
#ifdef PARISC
|
||||
// HP stack works from low addresses to high addresses
|
||||
// everyone else works the other way: from high addresses to low addresses
|
||||
stackTop = stack + 16; // HP requires 64-byte frame marker
|
||||
stackTop = stack + 16; // HP requires 64-byte frame marker
|
||||
stack[StackSize - 1] = STACK_FENCEPOST;
|
||||
#endif
|
||||
|
||||
#ifdef SPARC
|
||||
stackTop = stack + StackSize - 96; // SPARC stack must contains at
|
||||
// least 1 activation record
|
||||
// to start with.
|
||||
stackTop = stack + StackSize - 96; // SPARC stack must contains at
|
||||
// least 1 activation record
|
||||
// to start with.
|
||||
*stack = STACK_FENCEPOST;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PowerPC // RS6000
|
||||
stackTop = stack + StackSize - 16; // RS6000 requires 64-byte frame marker
|
||||
#ifdef PowerPC // RS6000
|
||||
stackTop = stack + StackSize - 16; // RS6000 requires 64-byte frame marker
|
||||
*stack = STACK_FENCEPOST;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#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;
|
||||
#endif
|
||||
|
||||
#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;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef x86
|
||||
// the x86 passes the return address on the stack. In order for SWITCH()
|
||||
// to go to ThreadRoot when we switch to this thread, the return addres
|
||||
// the x86 passes the return address on the stack. In order for SWITCH()
|
||||
// to go to ThreadRoot when we switch to this thread, the return addres
|
||||
// used in SWITCH() must be the starting address of ThreadRoot.
|
||||
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
|
||||
*(--stackTop) = (int) ThreadRoot;
|
||||
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
|
||||
*(--stackTop) = (int)ThreadRoot;
|
||||
*stack = STACK_FENCEPOST;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef PARISC
|
||||
machineState[PCState] = PLabelToAddr(ThreadRoot);
|
||||
machineState[StartupPCState] = PLabelToAddr(ThreadBegin);
|
||||
@@ -353,11 +351,11 @@ Thread::StackAllocate (VoidFunctionPtr func, void *arg)
|
||||
machineState[InitialArgState] = arg;
|
||||
machineState[WhenDonePCState] = PLabelToAddr(ThreadFinish);
|
||||
#else
|
||||
machineState[PCState] = (void*)ThreadRoot;
|
||||
machineState[StartupPCState] = (void*)ThreadBegin;
|
||||
machineState[InitialPCState] = (void*)func;
|
||||
machineState[InitialArgState] = (void*)arg;
|
||||
machineState[WhenDonePCState] = (void*)ThreadFinish;
|
||||
machineState[PCState] = (void *)ThreadRoot;
|
||||
machineState[StartupPCState] = (void *)ThreadBegin;
|
||||
machineState[InitialPCState] = (void *)func;
|
||||
machineState[InitialArgState] = (void *)arg;
|
||||
machineState[WhenDonePCState] = (void *)ThreadFinish;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -367,38 +365,35 @@ Thread::StackAllocate (VoidFunctionPtr func, void *arg)
|
||||
// Thread::SaveUserState
|
||||
// Save the CPU state of a user program on a context switch.
|
||||
//
|
||||
// Note that a user program thread has *two* sets of CPU registers --
|
||||
// one for its state while executing user code, one for its state
|
||||
// Note that a user program thread has *two* sets of CPU registers --
|
||||
// one for its state while executing user code, one for its state
|
||||
// while executing kernel code. This routine saves the former.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Thread::SaveUserState()
|
||||
void Thread::SaveUserState()
|
||||
{
|
||||
for (int i = 0; i < NumTotalRegs; i++)
|
||||
userRegisters[i] = kernel->machine->ReadRegister(i);
|
||||
userRegisters[i] = kernel->machine->ReadRegister(i);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Thread::RestoreUserState
|
||||
// Restore the CPU state of a user program on a context switch.
|
||||
//
|
||||
// Note that a user program thread has *two* sets of CPU registers --
|
||||
// one for its state while executing user code, one for its state
|
||||
// Note that a user program thread has *two* sets of CPU registers --
|
||||
// one for its state while executing user code, one for its state
|
||||
// while executing kernel code. This routine restores the former.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Thread::RestoreUserState()
|
||||
void Thread::RestoreUserState()
|
||||
{
|
||||
for (int i = 0; i < NumTotalRegs; i++)
|
||||
kernel->machine->WriteRegister(i, userRegisters[i]);
|
||||
kernel->machine->WriteRegister(i, userRegisters[i]);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// SimpleThread
|
||||
// Loop 5 times, yielding the CPU to another ready thread
|
||||
// Loop 5 times, yielding the CPU to another ready thread
|
||||
// each iteration.
|
||||
//
|
||||
// "which" is simply a number identifying the thread, for debugging
|
||||
@@ -409,27 +404,40 @@ static void
|
||||
SimpleThread(int which)
|
||||
{
|
||||
int num;
|
||||
|
||||
for (num = 0; num < 5; num++) {
|
||||
cout << "*** thread " << which << " looped " << num << " times\n";
|
||||
|
||||
for (num = 0; num < 5; num++)
|
||||
{
|
||||
cout << "*** thread " << which << " looped " << num << " times\n";
|
||||
kernel->currentThread->Yield();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Thread::SelfTest
|
||||
// Set up a ping-pong between two threads, by forking a thread
|
||||
// Set up a ping-pong between two threads, by forking a thread
|
||||
// to call SimpleThread, and then calling SimpleThread ourselves.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
Thread::SelfTest()
|
||||
void Thread::SelfTest()
|
||||
{
|
||||
DEBUG(dbgThread, "Entering Thread::SelfTest");
|
||||
|
||||
Thread *t = new Thread("forked thread", 1);
|
||||
|
||||
t->Fork((VoidFunctionPtr) SimpleThread, (void *) 1);
|
||||
t->Fork((VoidFunctionPtr)SimpleThread, (void *)1);
|
||||
kernel->currentThread->Yield();
|
||||
SimpleThread(0);
|
||||
}
|
||||
|
||||
// Todo ----
|
||||
int Thread::getPriority() const
|
||||
{
|
||||
return priority;
|
||||
}
|
||||
|
||||
void Thread::setPriority(int p)
|
||||
{
|
||||
ASSERT(p >= 0 && p <= 149);
|
||||
priority = p;
|
||||
}
|
||||
// ---------
|
||||
|
||||
@@ -80,6 +80,10 @@ class Thread {
|
||||
int *stackTop; // the current stack pointer
|
||||
void *machineState[MachineStateSize]; // all registers except for stackTop
|
||||
|
||||
// Todo ----
|
||||
int priority;
|
||||
// ---------
|
||||
|
||||
public:
|
||||
Thread(char* debugName, int threadID); // initialize a Thread
|
||||
~Thread(); // deallocate a Thread
|
||||
@@ -89,6 +93,11 @@ class Thread {
|
||||
|
||||
// basic thread operations
|
||||
|
||||
// Todo ----
|
||||
int getPriority() const;
|
||||
void setPriority(int p);
|
||||
// ---------
|
||||
|
||||
void Fork(VoidFunctionPtr func, void *arg);
|
||||
// Make thread run (*func)(arg)
|
||||
void Yield(); // Relinquish the CPU if any
|
||||
@@ -97,32 +106,32 @@ class Thread {
|
||||
// relinquish the processor
|
||||
void Begin(); // Startup code for the thread
|
||||
void Finish(); // The thread is done executing
|
||||
|
||||
|
||||
void CheckOverflow(); // Check if thread stack has overflowed
|
||||
void setStatus(ThreadStatus st) { status = st; }
|
||||
ThreadStatus getStatus() { return (status); }
|
||||
char* getName() { return (name); }
|
||||
|
||||
int getID() { return (ID); }
|
||||
char* getName() { return (name); }
|
||||
|
||||
int getID() { return (ID); }
|
||||
void Print() { cout << name; }
|
||||
void SelfTest(); // test whether thread impl is working
|
||||
|
||||
private:
|
||||
// some of the private data for this class is listed above
|
||||
|
||||
|
||||
int *stack; // Bottom of the stack
|
||||
// NULL if this is the main thread
|
||||
// (If NULL, don't deallocate stack)
|
||||
// NULL if this is the main thread
|
||||
// (If NULL, don't deallocate stack)
|
||||
ThreadStatus status; // ready, running or blocked
|
||||
char* name;
|
||||
int ID;
|
||||
int ID;
|
||||
void StackAllocate(VoidFunctionPtr func, void *arg);
|
||||
// Allocate a stack for thread.
|
||||
// Used internally by Fork()
|
||||
// Allocate a stack for thread.
|
||||
// Used internally by Fork()
|
||||
|
||||
// A thread running a user program actually has *two* sets of CPU registers --
|
||||
// one for its state while executing user code, one for its state
|
||||
// while executing kernel code.
|
||||
// A thread running a user program actually has *two* sets of CPU registers --
|
||||
// one for its state while executing user code, one for its state
|
||||
// while executing kernel code.
|
||||
|
||||
int userRegisters[NumTotalRegs]; // user-level CPU register state
|
||||
|
||||
|
||||
@@ -27,9 +27,8 @@
|
||||
// object file header, in case the file was generated on a little
|
||||
// endian machine, and we're now running on a big endian machine.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
SwapHeader (NoffHeader *noffH)
|
||||
static void
|
||||
SwapHeader(NoffHeader* noffH)
|
||||
{
|
||||
noffH->noffMagic = WordToHost(noffH->noffMagic);
|
||||
noffH->code.size = WordToHost(noffH->code.size);
|
||||
@@ -37,10 +36,10 @@ SwapHeader (NoffHeader *noffH)
|
||||
noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr);
|
||||
#ifdef RDATA
|
||||
noffH->readonlyData.size = WordToHost(noffH->readonlyData.size);
|
||||
noffH->readonlyData.virtualAddr =
|
||||
WordToHost(noffH->readonlyData.virtualAddr);
|
||||
noffH->readonlyData.inFileAddr =
|
||||
WordToHost(noffH->readonlyData.inFileAddr);
|
||||
noffH->readonlyData.virtualAddr =
|
||||
WordToHost(noffH->readonlyData.virtualAddr);
|
||||
noffH->readonlyData.inFileAddr =
|
||||
WordToHost(noffH->readonlyData.inFileAddr);
|
||||
#endif
|
||||
noffH->initData.size = WordToHost(noffH->initData.size);
|
||||
noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr);
|
||||
@@ -50,10 +49,10 @@ SwapHeader (NoffHeader *noffH)
|
||||
noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr);
|
||||
|
||||
#ifdef RDATA
|
||||
DEBUG(dbgAddr, "code = " << noffH->code.size <<
|
||||
" readonly = " << noffH->readonlyData.size <<
|
||||
" init = " << noffH->initData.size <<
|
||||
" uninit = " << noffH->uninitData.size << "\n");
|
||||
DEBUG(dbgAddr, "code = " << noffH->code.size <<
|
||||
" readonly = " << noffH->readonlyData.size <<
|
||||
" init = " << noffH->initData.size <<
|
||||
" uninit = " << noffH->uninitData.size << "\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -64,21 +63,23 @@ SwapHeader (NoffHeader *noffH)
|
||||
// memory. For now, this is really simple (1:1), since we are
|
||||
// only uniprogramming, and we have a single unsegmented page table
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
AddrSpace::AddrSpace()
|
||||
{
|
||||
pageTable = new TranslationEntry[NumPhysPages];
|
||||
for (int i = 0; i < NumPhysPages; i++) {
|
||||
pageTable[i].virtualPage = i; // for now, virt page # = phys page #
|
||||
pageTable[i].physicalPage = i;
|
||||
pageTable[i].valid = TRUE;
|
||||
pageTable[i].use = FALSE;
|
||||
pageTable[i].dirty = FALSE;
|
||||
pageTable[i].readOnly = FALSE;
|
||||
}
|
||||
|
||||
// zero out the entire address space
|
||||
bzero(kernel->machine->mainMemory, MemorySize);
|
||||
pageTable = NULL; // initialize with NULL
|
||||
/*
|
||||
pageTable = new TranslationEntry[NumPhysPages];
|
||||
for (int i = 0; i < NumPhysPages; i++) {
|
||||
pageTable[i].virtualPage = i; // for now, virt page # = phys page #
|
||||
pageTable[i].physicalPage = i;
|
||||
pageTable[i].valid = TRUE;
|
||||
pageTable[i].use = FALSE;
|
||||
pageTable[i].dirty = FALSE;
|
||||
pageTable[i].readOnly = FALSE;
|
||||
}
|
||||
|
||||
// zero out the entire address space
|
||||
bzero(kernel->machine->mainMemory, MemorySize);
|
||||
*/
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -88,7 +89,9 @@ 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
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
AddrSpace::Load(char *fileName)
|
||||
bool
|
||||
AddrSpace::Load(char* fileName)
|
||||
{
|
||||
OpenFile *executable = kernel->fileSystem->Open(fileName);
|
||||
OpenFile* executable = kernel->fileSystem->Open(fileName);
|
||||
NoffHeader noffH;
|
||||
unsigned int size;
|
||||
|
||||
if (executable == NULL) {
|
||||
cerr << "Unable to open file " << fileName << "\n";
|
||||
return FALSE;
|
||||
cerr << "Unable to open file " << fileName << "\n";
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
executable->ReadAt((char *)&noffH, sizeof(noffH), 0);
|
||||
if ((noffH.noffMagic != NOFFMAGIC) &&
|
||||
(WordToHost(noffH.noffMagic) == NOFFMAGIC))
|
||||
SwapHeader(&noffH);
|
||||
executable->ReadAt((char*)&noffH, sizeof(noffH), 0);
|
||||
if ((noffH.noffMagic != NOFFMAGIC) &&
|
||||
(WordToHost(noffH.noffMagic) == NOFFMAGIC))
|
||||
SwapHeader(&noffH);
|
||||
ASSERT(noffH.noffMagic == NOFFMAGIC);
|
||||
|
||||
#ifdef RDATA
|
||||
// how big is address space?
|
||||
// how big is address space?
|
||||
size = noffH.code.size + noffH.readonlyData.size + noffH.initData.size +
|
||||
noffH.uninitData.size + UserStackSize;
|
||||
// we need to increase the size
|
||||
// to leave room for the stack
|
||||
noffH.uninitData.size + UserStackSize;
|
||||
// we need to increase the size
|
||||
// to leave room for the stack
|
||||
#else
|
||||
// how big is address space?
|
||||
size = noffH.code.size + noffH.initData.size + noffH.uninitData.size
|
||||
+ UserStackSize; // we need to increase the size
|
||||
// to leave room for the stack
|
||||
// how big is address space?
|
||||
size = noffH.code.size + noffH.initData.size + noffH.uninitData.size
|
||||
+ UserStackSize; // we need to increase the size
|
||||
// to leave room for the stack
|
||||
#endif
|
||||
numPages = divRoundUp(size, PageSize);
|
||||
size = numPages * PageSize;
|
||||
|
||||
ASSERT(numPages <= NumPhysPages); // check we're not trying
|
||||
// to run anything too big --
|
||||
// at least until we have
|
||||
// virtual memory
|
||||
pageTable = kernel->frameTable->Allocate(numPages);
|
||||
if (!pageTable) {
|
||||
kernel->interrupt->setStatus(SystemMode);
|
||||
ExceptionHandler(MemoryLimitException);
|
||||
kernel->interrupt->setStatus(UserMode);
|
||||
}
|
||||
|
||||
DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size);
|
||||
|
||||
// then, copy in the code and data segments into memory
|
||||
// Note: this code assumes that virtual address = physical address
|
||||
// then, copy in the code and data segments into memory
|
||||
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) {
|
||||
DEBUG(dbgAddr, "Initializing code segment.");
|
||||
DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
|
||||
executable->ReadAt(
|
||||
&(kernel->machine->mainMemory[noffH.code.virtualAddr]),
|
||||
noffH.code.size, noffH.code.inFileAddr);
|
||||
DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
|
||||
|
||||
sz = noffH.code.size;
|
||||
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) {
|
||||
DEBUG(dbgAddr, "Initializing data segment.");
|
||||
DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
|
||||
executable->ReadAt(
|
||||
&(kernel->machine->mainMemory[noffH.initData.virtualAddr]),
|
||||
noffH.initData.size, noffH.initData.inFileAddr);
|
||||
DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
|
||||
|
||||
sz = noffH.initData.size;
|
||||
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
|
||||
if (noffH.readonlyData.size > 0) {
|
||||
DEBUG(dbgAddr, "Initializing read only data segment.");
|
||||
DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
|
||||
executable->ReadAt(
|
||||
&(kernel->machine->mainMemory[noffH.readonlyData.virtualAddr]),
|
||||
noffH.readonlyData.size, noffH.readonlyData.inFileAddr);
|
||||
DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
|
||||
|
||||
sz = noffH.readonlyData.size;
|
||||
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
|
||||
|
||||
@@ -181,9 +248,8 @@ AddrSpace::Load(char *fileName)
|
||||
// the address space
|
||||
//
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
AddrSpace::Execute(char* fileName)
|
||||
void
|
||||
AddrSpace::Execute(char* fileName)
|
||||
{
|
||||
|
||||
kernel->currentThread->space = this;
|
||||
@@ -191,11 +257,11 @@ AddrSpace::Execute(char* fileName)
|
||||
this->InitRegisters(); // set the initial register values
|
||||
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;
|
||||
// the address space exits
|
||||
// by doing the syscall "exit"
|
||||
// the address space exits
|
||||
// by doing the syscall "exit"
|
||||
}
|
||||
|
||||
|
||||
@@ -208,19 +274,18 @@ AddrSpace::Execute(char* fileName)
|
||||
// will be saved/restored into the currentThread->userRegisters
|
||||
// when this thread is context switched out.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
AddrSpace::InitRegisters()
|
||||
{
|
||||
Machine *machine = kernel->machine;
|
||||
Machine* machine = kernel->machine;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NumTotalRegs; i++)
|
||||
machine->WriteRegister(i, 0);
|
||||
machine->WriteRegister(i, 0);
|
||||
|
||||
// Initial program counter -- must be location of "Start", which
|
||||
// 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
|
||||
// of branch delay possibility
|
||||
@@ -228,9 +293,9 @@ AddrSpace::InitRegisters()
|
||||
// after start will be at virtual address four.
|
||||
machine->WriteRegister(NextPCReg, 4);
|
||||
|
||||
// Set the stack register to the end of the address space, where we
|
||||
// allocated the stack; but subtract off a bit, to make sure we don't
|
||||
// accidentally reference off the end!
|
||||
// Set the stack register to the end of the address space, where we
|
||||
// allocated the stack; but subtract off a bit, to make sure we don't
|
||||
// accidentally reference off the end!
|
||||
machine->WriteRegister(StackReg, numPages * PageSize - 16);
|
||||
DEBUG(dbgAddr, "Initializing stack pointer: " << numPages * PageSize - 16);
|
||||
}
|
||||
@@ -242,9 +307,9 @@ AddrSpace::InitRegisters()
|
||||
//
|
||||
// For now, don't need to save anything!
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void AddrSpace::SaveState()
|
||||
{}
|
||||
void AddrSpace::SaveState()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::RestoreState
|
||||
@@ -253,8 +318,7 @@ void AddrSpace::SaveState()
|
||||
//
|
||||
// For now, tell the machine where to find the page table.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void AddrSpace::RestoreState()
|
||||
void AddrSpace::RestoreState()
|
||||
{
|
||||
kernel->machine->pageTable = pageTable;
|
||||
kernel->machine->pageTableSize = numPages;
|
||||
@@ -270,20 +334,20 @@ void AddrSpace::RestoreState()
|
||||
// Return any exceptions caused by the address translation.
|
||||
//----------------------------------------------------------------------
|
||||
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;
|
||||
unsigned int vpn = vaddr / PageSize;
|
||||
unsigned int vpn = vaddr / PageSize;
|
||||
unsigned int offset = vaddr % PageSize;
|
||||
|
||||
if(vpn >= numPages) {
|
||||
if (vpn >= numPages) {
|
||||
return AddressErrorException;
|
||||
}
|
||||
|
||||
pte = &pageTable[vpn];
|
||||
|
||||
if(isReadWrite && pte->readOnly) {
|
||||
if (isReadWrite && pte->readOnly) {
|
||||
return ReadOnlyException;
|
||||
}
|
||||
|
||||
@@ -298,10 +362,10 @@ AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
|
||||
|
||||
pte->use = TRUE; // set the use, dirty bits
|
||||
|
||||
if(isReadWrite)
|
||||
if (isReadWrite)
|
||||
pte->dirty = TRUE;
|
||||
|
||||
*paddr = pfn*PageSize + offset;
|
||||
*paddr = pfn * PageSize + offset;
|
||||
|
||||
ASSERT((*paddr < MemorySize));
|
||||
|
||||
@@ -311,6 +375,43 @@ AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,35 +19,83 @@
|
||||
#define UserStackSize 1024 // increase this as necessary!
|
||||
|
||||
class AddrSpace {
|
||||
public:
|
||||
AddrSpace(); // Create an address space.
|
||||
~AddrSpace(); // De-allocate an address space
|
||||
public:
|
||||
AddrSpace(); // Create an address space.
|
||||
~AddrSpace(); // De-allocate an address space
|
||||
|
||||
bool Load(char *fileName); // Load a program into addr space from
|
||||
// a file
|
||||
// return false if not found
|
||||
bool Load(char* fileName); // Load a program into addr space from
|
||||
// a file
|
||||
// return false if not found
|
||||
|
||||
void Execute(char *fileName); // Run a program
|
||||
// assumes the program has already
|
||||
// been loaded
|
||||
void Execute(char* fileName); // Run a program
|
||||
// assumes the program has already
|
||||
// been loaded
|
||||
|
||||
void SaveState(); // Save/restore address space-specific
|
||||
void RestoreState(); // info on a context switch
|
||||
void SaveState(); // Save/restore address space-specific
|
||||
void RestoreState(); // info on a context switch
|
||||
|
||||
// Translate virtual address _vaddr_
|
||||
// to physical address _paddr_. _mode_
|
||||
// is 0 for Read, 1 for Write.
|
||||
ExceptionType Translate(unsigned int vaddr, unsigned int *paddr, int mode);
|
||||
// Translate virtual address _vaddr_
|
||||
// to physical address _paddr_. _mode_
|
||||
// is 0 for Read, 1 for Write.
|
||||
ExceptionType Translate(unsigned int vaddr, unsigned int* paddr, int mode);
|
||||
|
||||
private:
|
||||
TranslationEntry *pageTable; // Assume linear page table translation
|
||||
// for now!
|
||||
unsigned int numPages; // Number of pages in the virtual
|
||||
// address space
|
||||
private:
|
||||
TranslationEntry* pageTable; // Assume linear page table translation
|
||||
// for now!
|
||||
unsigned int numPages; // Number of pages in the virtual
|
||||
// address space
|
||||
|
||||
void InitRegisters(); // Initialize user-level CPU registers,
|
||||
// before jumping to user code
|
||||
void InitRegisters(); // Initialize user-level CPU registers,
|
||||
// before jumping to user code
|
||||
|
||||
};
|
||||
|
||||
#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 */
|
||||
@@ -51,74 +51,138 @@
|
||||
void
|
||||
ExceptionHandler(ExceptionType which)
|
||||
{
|
||||
int type = kernel->machine->ReadRegister(2);
|
||||
int type = kernel->machine->ReadRegister(2);
|
||||
int val;
|
||||
int status, exit, threadID, programID;
|
||||
int status, exit, threadID, programID;
|
||||
int fd, size;
|
||||
DEBUG(dbgSys, "Received Exception " << which << " type: " << type << "\n");
|
||||
switch (which) {
|
||||
case SyscallException:
|
||||
switch(type) {
|
||||
case SC_Halt:
|
||||
switch (which) {
|
||||
case SyscallException:
|
||||
switch (type) {
|
||||
case SC_Halt:
|
||||
DEBUG(dbgSys, "Shutdown, initiated by user program.\n");
|
||||
SysHalt();
|
||||
cout<<"in exception\n";
|
||||
cout << "in exception\n";
|
||||
ASSERTNOTREACHED();
|
||||
break;
|
||||
case SC_MSG:
|
||||
DEBUG(dbgSys, "Message received.\n");
|
||||
val = kernel->machine->ReadRegister(4);
|
||||
{
|
||||
char *msg = &(kernel->machine->mainMemory[val]);
|
||||
cout << msg << endl;
|
||||
char* msg = &(kernel->machine->mainMemory[val]);
|
||||
cout << msg << endl;
|
||||
}
|
||||
SysHalt();
|
||||
ASSERTNOTREACHED();
|
||||
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:
|
||||
val = kernel->machine->ReadRegister(4);
|
||||
{
|
||||
char *filename = &(kernel->machine->mainMemory[val]);
|
||||
//cout << filename << endl;
|
||||
status = SysCreate(filename);
|
||||
kernel->machine->WriteRegister(2, (int) status);
|
||||
char* filename = &(kernel->machine->mainMemory[val]);
|
||||
//cout << filename << endl;
|
||||
status = SysCreate(filename);
|
||||
kernel->machine->WriteRegister(2, (int)status);
|
||||
}
|
||||
kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
|
||||
kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
|
||||
kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg)+4);
|
||||
kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4);
|
||||
return;
|
||||
ASSERTNOTREACHED();
|
||||
break;
|
||||
case SC_Add:
|
||||
break;
|
||||
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");
|
||||
/* Process SysAdd Systemcall*/
|
||||
int result;
|
||||
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");
|
||||
/* Prepare Result */
|
||||
kernel->machine->WriteRegister(2, (int)result);
|
||||
kernel->machine->WriteRegister(2, (int)result);
|
||||
/* Modify return point */
|
||||
{
|
||||
/* set previous programm counter (debugging only)*/
|
||||
kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
|
||||
|
||||
/* set programm counter to next instruction (all Instructions are 4 byte wide)*/
|
||||
kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
|
||||
|
||||
/* set next programm counter for brach execution */
|
||||
kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg)+4);
|
||||
/* set previous programm counter (debugging only)*/
|
||||
kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg));
|
||||
|
||||
/* set programm counter to next instruction (all Instructions are 4 byte wide)*/
|
||||
kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4);
|
||||
|
||||
/* set next programm counter for brach execution */
|
||||
kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4);
|
||||
}
|
||||
cout << "result is " << result << "\n";
|
||||
return;
|
||||
cout << "result is " << result << "\n";
|
||||
return;
|
||||
ASSERTNOTREACHED();
|
||||
break;
|
||||
break;
|
||||
case SC_Exit:
|
||||
DEBUG(dbgAddr, "Program exit\n");
|
||||
val=kernel->machine->ReadRegister(4);
|
||||
cout << "return value:" << val << endl;
|
||||
val = kernel->machine->ReadRegister(4);
|
||||
cout << "return value:" << val << endl;
|
||||
kernel->currentThread->Finish();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
cerr << "Unexpected system call " << type << "\n";
|
||||
break;
|
||||
}
|
||||
@@ -126,7 +190,7 @@ ExceptionHandler(ExceptionType which)
|
||||
default:
|
||||
cerr << "Unexpected user mode exception " << (int)which << "\n";
|
||||
break;
|
||||
}
|
||||
ASSERTNOTREACHED();
|
||||
}
|
||||
ASSERTNOTREACHED();
|
||||
}
|
||||
|
||||
|
||||
11
code/userprog/hw4_consoleIO_1.c
Normal file
11
code/userprog/hw4_consoleIO_1.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "syscall.h"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < 4; n++) {
|
||||
PrintInt(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
11
code/userprog/hw4_consoleIO_2.c
Normal file
11
code/userprog/hw4_consoleIO_2.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "syscall.h"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int n;
|
||||
for (n = 0; n < 5; n++) {
|
||||
PrintInt(2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
12
code/userprog/hw4_consoleIO_3.c
Normal file
12
code/userprog/hw4_consoleIO_3.c
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "syscall.h"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 12; n++) {
|
||||
PrintInt(3);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
12
code/userprog/hw4_consoleIO_4.c
Normal file
12
code/userprog/hw4_consoleIO_4.c
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "syscall.h"
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 11; n++) {
|
||||
PrintInt(4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
*
|
||||
* userprog/ksyscall.h
|
||||
*
|
||||
* Kernel interface for systemcalls
|
||||
* Kernel interface for systemcalls
|
||||
*
|
||||
* by Marcus Voelp (c) Universitaet Karlsruhe
|
||||
*
|
||||
@@ -15,18 +15,19 @@
|
||||
|
||||
#include "synchconsole.h"
|
||||
|
||||
typedef int OpenFileId;
|
||||
|
||||
void SysHalt()
|
||||
{
|
||||
kernel->interrupt->Halt();
|
||||
kernel->interrupt->Halt();
|
||||
}
|
||||
|
||||
int SysAdd(int op1, int op2)
|
||||
{
|
||||
return op1 + op2;
|
||||
return op1 + op2;
|
||||
}
|
||||
|
||||
int SysCreate(char *filename)
|
||||
int SysCreate(char* filename)
|
||||
{
|
||||
// return value
|
||||
// 1: success
|
||||
@@ -34,5 +35,37 @@ int SysCreate(char *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__ */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* noff.h
|
||||
/* noff.h
|
||||
* Data structures defining the Nachos Object Code Format
|
||||
*
|
||||
* Basically, we only know about three types of segments:
|
||||
@@ -6,23 +6,22 @@
|
||||
*/
|
||||
|
||||
#define NOFFMAGIC 0xbadfad /* magic number denoting Nachos
|
||||
* object code file
|
||||
*/
|
||||
/* object code file*/
|
||||
|
||||
typedef struct segment {
|
||||
int virtualAddr; /* location of segment in virt addr space */
|
||||
int inFileAddr; /* location of segment in this file */
|
||||
int size; /* size of segment */
|
||||
int virtualAddr; /* location of segment in virt addr space */
|
||||
int inFileAddr; /* location of segment in this file */
|
||||
int size; /* size of segment */
|
||||
} Segment;
|
||||
|
||||
typedef struct noffHeader {
|
||||
int noffMagic; /* should be NOFFMAGIC */
|
||||
Segment code; /* executable code segment */
|
||||
Segment code; /* executable code segment */
|
||||
Segment initData; /* initialized data segment */
|
||||
#ifdef RDATA
|
||||
Segment readonlyData; /* read only data */
|
||||
#endif
|
||||
Segment uninitData; /* uninitialized data segment --
|
||||
* should be zero'ed before use
|
||||
*/
|
||||
* should be zero'ed before use
|
||||
*/
|
||||
} NoffHeader;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// otherwise, read from this file
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
SynchConsoleInput::SynchConsoleInput(char *inputFile)
|
||||
SynchConsoleInput::SynchConsoleInput(char* inputFile)
|
||||
{
|
||||
consoleInput = new ConsoleInput(inputFile, this);
|
||||
lock = new Lock("console in");
|
||||
@@ -30,9 +30,9 @@ SynchConsoleInput::SynchConsoleInput(char *inputFile)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
SynchConsoleInput::~SynchConsoleInput()
|
||||
{
|
||||
delete consoleInput;
|
||||
delete lock;
|
||||
{
|
||||
delete consoleInput;
|
||||
delete lock;
|
||||
delete waitFor;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ SynchConsoleInput::CallBack()
|
||||
// otherwise, read from this file
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
SynchConsoleOutput::SynchConsoleOutput(char *outputFile)
|
||||
SynchConsoleOutput::SynchConsoleOutput(char* outputFile)
|
||||
{
|
||||
consoleOutput = new ConsoleOutput(outputFile, this);
|
||||
lock = new Lock("console out");
|
||||
@@ -86,9 +86,9 @@ SynchConsoleOutput::SynchConsoleOutput(char *outputFile)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
SynchConsoleOutput::~SynchConsoleOutput()
|
||||
{
|
||||
delete consoleOutput;
|
||||
delete lock;
|
||||
{
|
||||
delete consoleOutput;
|
||||
delete lock;
|
||||
delete waitFor;
|
||||
}
|
||||
|
||||
@@ -106,6 +106,20 @@ SynchConsoleOutput::PutChar(char ch)
|
||||
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
|
||||
// Interrupt handler called when it's safe to send the next
|
||||
|
||||
@@ -21,33 +21,34 @@
|
||||
// a console device
|
||||
|
||||
class SynchConsoleInput : public CallBackObj {
|
||||
public:
|
||||
SynchConsoleInput(char *inputFile); // Initialize the console device
|
||||
~SynchConsoleInput(); // Deallocate console device
|
||||
public:
|
||||
SynchConsoleInput(char* inputFile); // Initialize the console device
|
||||
~SynchConsoleInput(); // Deallocate console device
|
||||
|
||||
char GetChar(); // Read a character, waiting if necessary
|
||||
|
||||
private:
|
||||
ConsoleInput *consoleInput; // the hardware keyboard
|
||||
Lock *lock; // only one reader at a time
|
||||
Semaphore *waitFor; // wait for callBack
|
||||
char GetChar(); // Read a character, waiting if necessary
|
||||
|
||||
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 {
|
||||
public:
|
||||
SynchConsoleOutput(char *outputFile); // Initialize the console device
|
||||
~SynchConsoleOutput();
|
||||
public:
|
||||
SynchConsoleOutput(char* outputFile); // Initialize the console device
|
||||
~SynchConsoleOutput();
|
||||
|
||||
void PutChar(char ch); // Write a character, waiting if necessary
|
||||
|
||||
private:
|
||||
ConsoleOutput *consoleOutput;// the hardware display
|
||||
Lock *lock; // only one writer at a time
|
||||
Semaphore *waitFor; // wait for callBack
|
||||
void PutChar(char ch); // Write a character, waiting if necessary
|
||||
void PutInt(int value);
|
||||
|
||||
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
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* syscalls.h
|
||||
/* syscalls.h
|
||||
* Nachos system call interface. These are Nachos kernel operations
|
||||
* that can be invoked from user programs, by trapping to the kernel
|
||||
* via the "syscall" instruction.
|
||||
*
|
||||
* This file is included by user programs and by the Nachos kernel.
|
||||
* This file is included by user programs and by the Nachos kernel.
|
||||
*
|
||||
* Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
* All rights reserved. See copyright.h for copyright notice and limitation
|
||||
* All rights reserved. See copyright.h for copyright notice and limitation
|
||||
* of liability and disclaimer of warranty provisions.
|
||||
*/
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
|
||||
#include "copyright.h"
|
||||
#include "errno.h"
|
||||
/* system call codes -- used by the stubs to tell the kernel which system call
|
||||
* is being asked for
|
||||
*/
|
||||
/* system call codes -- used by the stubs to tell the kernel which system call
|
||||
* is being asked for
|
||||
*/
|
||||
#define SC_Halt 0
|
||||
#define SC_Exit 1
|
||||
#define SC_Exec 2
|
||||
@@ -36,59 +36,65 @@
|
||||
#define SC_ThreadJoin 15
|
||||
#define SC_Add 42
|
||||
#define SC_MSG 100
|
||||
#define SC_PrintInt 16
|
||||
|
||||
#ifndef IN_ASM
|
||||
|
||||
/* The system call interface. These are the operations the Nachos
|
||||
* kernel needs to support, to be able to run user programs.
|
||||
*
|
||||
* Each of these is invoked by a user program by simply calling the
|
||||
* procedure; an assembly language stub stuffs the system call code
|
||||
* into a register, and traps to the kernel. The kernel procedures
|
||||
* are then invoked in the Nachos kernel, after appropriate error checking,
|
||||
* from the system call entry point in exception.cc.
|
||||
*/
|
||||
/* The system call interface. These are the operations the Nachos
|
||||
* kernel needs to support, to be able to run user programs.
|
||||
*
|
||||
* Each of these is invoked by a user program by simply calling the
|
||||
* procedure; an assembly language stub stuffs the system call code
|
||||
* into a register, and traps to the kernel. The kernel procedures
|
||||
* are then invoked in the Nachos kernel, after appropriate error checking,
|
||||
* from the system call entry point in exception.cc.
|
||||
*/
|
||||
|
||||
/* Stop Nachos, and print out performance stats */
|
||||
void Halt();
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
*/
|
||||
|
||||
int Add(int op1, int op2);
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
/* 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) */
|
||||
typedef int SpaceId;
|
||||
typedef int SpaceId;
|
||||
|
||||
/* A unique identifier for a thread within a task */
|
||||
typedef int ThreadId;
|
||||
|
||||
/* Run the specified executable, with no args */
|
||||
/* This can be implemented as a call to ExecV.
|
||||
*/
|
||||
*/
|
||||
SpaceId Exec(char* exec_name);
|
||||
|
||||
/* Run the executable, stored in the Nachos file "argv[0]", with
|
||||
* parameters stored in argv[1..argc-1] and return the
|
||||
* parameters stored in argv[1..argc-1] and return the
|
||||
* address space identifier
|
||||
*/
|
||||
SpaceId ExecV(int argc, char* argv[]);
|
||||
|
||||
/* Only return once the user program "id" has finished.
|
||||
|
||||
/* Only return once the user program "id" has finished.
|
||||
* Return the exit status.
|
||||
*/
|
||||
int Join(SpaceId id);
|
||||
|
||||
int Join(SpaceId id);
|
||||
|
||||
|
||||
/* File system operations: Create, Remove, Open, Read, Write, Close
|
||||
* 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
|
||||
* 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).
|
||||
* Read and Write can be used directly on these, without first opening
|
||||
* the console device.
|
||||
@@ -110,33 +116,33 @@ typedef int OpenFileId;
|
||||
|
||||
#define SysConsoleInput 0
|
||||
#define SysConsoleOutput 1
|
||||
|
||||
/* Create a Nachos file, with name "name" */
|
||||
/* Note: Create does not open the file. */
|
||||
/* Return 1 on success, negative error code on failure */
|
||||
int Create(char *name);
|
||||
|
||||
/* Create a Nachos file, with name "name" */
|
||||
/* Note: Create does not open the file. */
|
||||
/* Return 1 on success, negative error code on failure */
|
||||
int Create(char* name);
|
||||
|
||||
/* Remove a Nachos file, with name "name" */
|
||||
int Remove(char *name);
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
* 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
|
||||
* 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,
|
||||
* long enough, or if it is an I/O device, and there aren't enough
|
||||
* characters to read, return whatever is available (for I/O devices,
|
||||
* you should always wait until you can return at least one character).
|
||||
*/
|
||||
int Read(char *buffer, int size, OpenFileId id);
|
||||
int Read(char* buffer, int size, OpenFileId id);
|
||||
|
||||
/* Set the seek position of the open file "id"
|
||||
* to the byte "position".
|
||||
@@ -150,21 +156,21 @@ int Close(OpenFileId id);
|
||||
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/* Fork a thread to run a procedure ("func") in the *same* address space
|
||||
* as the current thread.
|
||||
* Return a positive ThreadId on success, negative error code on failure
|
||||
*/
|
||||
/* Fork a thread to run a procedure ("func") in the *same* address space
|
||||
* as the current thread.
|
||||
* Return a positive ThreadId on success, negative error code on failure
|
||||
*/
|
||||
ThreadId ThreadFork(void (*func)());
|
||||
|
||||
/* Yield the CPU to another runnable thread, whether in this address space
|
||||
* or not.
|
||||
/* Yield the CPU to another runnable thread, whether in this address space
|
||||
* or not.
|
||||
*/
|
||||
void ThreadYield();
|
||||
void ThreadYield();
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
void ThreadExit(int ExitCode);
|
||||
void ThreadExit(int ExitCode);
|
||||
|
||||
#endif /* IN_ASM */
|
||||
|
||||
|
||||
7
docker-compose.yaml
Normal file
7
docker-compose.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
services:
|
||||
test:
|
||||
build: .
|
||||
user: ytshih
|
||||
volumes:
|
||||
- './:/work'
|
||||
Reference in New Issue
Block a user