This commit is contained in:
2024-12-30 05:59:42 +08:00
parent bf78b95c9d
commit 72320ede22
42 changed files with 1758 additions and 1556 deletions

View File

@@ -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();
}
// ---------
}

View File

@@ -24,11 +24,9 @@
// for the initialization (see also comments in main.cc)
//----------------------------------------------------------------------
Kernel::Kernel(int argc, char **argv)
Kernel::Kernel(int argc, char** argv)
{
execfileNum = 0;
threadNum = 0;
randomSlice = FALSE;
randomSlice = FALSE;
debugUserProg = FALSE;
consoleIn = NULL; // default is stdin
consoleOut = NULL; // default is stdout
@@ -45,32 +43,48 @@ Kernel::Kernel(int argc, char **argv)
// number generator
randomSlice = TRUE;
i++;
} else if (strcmp(argv[i], "-s") == 0) {
}
else if (strcmp(argv[i], "-s") == 0) {
debugUserProg = TRUE;
} else if (strcmp(argv[i], "-e") == 0) {
execfile[++execfileNum]= argv[++i];
}
// Todo ----
else if (strcmp(argv[i], "-e") == 0) {
execfile[++execfileNum] = argv[++i];
cout << execfile[execfileNum] << "\n";
} else if (strcmp(argv[i], "-ci") == 0) {
}
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) {
}
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) {
}
else if (strcmp(argv[i], "-f") == 0) {
formatFlag = TRUE;
#endif
} else if (strcmp(argv[i], "-n") == 0) {
}
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) {
}
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) {
}
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";
@@ -92,32 +106,34 @@ Kernel::Kernel(int argc, char **argv)
void
Kernel::Initialize()
{
// We didn't explicitly allocate the current thread we are running in.
// But if it ever tries to give up the CPU, we better have a Thread
// object to save its state.
// We didn't explicitly allocate the current thread we are running in.
// But if it ever tries to give up the CPU, we better have a Thread
// object to save its state.
currentThread = new Thread("main", threadNum++);
currentThread->setStatus(RUNNING);
currentThread = new Thread("main", threadNum++);
currentThread->setStatus(RUNNING);
stats = new Statistics(); // collect statistics
interrupt = new Interrupt; // start up interrupt handling
scheduler = new Scheduler(); // initialize the ready queue
alarm = new Alarm(randomSlice); // start up time slicing
machine = new Machine(debugUserProg);
synchConsoleIn = new SynchConsoleInput(consoleIn); // input from stdin
synchConsoleOut = new SynchConsoleOutput(consoleOut); // output to stdout
synchDisk = new SynchDisk(); //
stats = new Statistics(); // collect statistics
interrupt = new Interrupt; // start up interrupt handling
scheduler = new Scheduler(); // initialize the ready queue
alarm = new Alarm(randomSlice); // start up time slicing
machine = new Machine(debugUserProg);
synchConsoleIn = new SynchConsoleInput(consoleIn); // input from stdin
synchConsoleOut = new SynchConsoleOutput(consoleOut); // output to stdout
synchDisk = new SynchDisk(); //
#ifdef FILESYS_STUB
fileSystem = new FileSystem();
fileSystem = new FileSystem();
#else
fileSystem = new FileSystem(formatFlag);
fileSystem = new FileSystem(formatFlag);
#endif // FILESYS_STUB
postOfficeIn = new PostOfficeInput(10);
postOfficeOut = new PostOfficeOutput(reliability);
frameTable = new FrameTable;
interrupt->Enable();
// MP4 MODIFIED
// postOfficeIn = new PostOfficeInput(10);
// postOfficeOut = new PostOfficeOutput(reliability);
frameTable = new FrameTable();
interrupt->Enable();
}
//----------------------------------------------------------------------
@@ -127,20 +143,23 @@ Kernel::Initialize()
Kernel::~Kernel()
{
delete stats;
delete interrupt;
delete scheduler;
delete alarm;
delete machine;
delete synchConsoleIn;
delete synchConsoleOut;
delete synchDisk;
delete fileSystem;
delete postOfficeIn;
delete postOfficeOut;
delete frameTable;
delete stats;
delete interrupt;
delete scheduler;
delete alarm;
delete machine;
delete synchConsoleIn;
delete synchConsoleOut;
delete synchDisk;
delete fileSystem;
Exit(0);
// MP4 MODIFIED
// delete postOfficeIn;
// delete postOfficeOut;
delete frameTable;
Exit(0);
}
//----------------------------------------------------------------------
@@ -150,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;
}
@@ -179,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";
@@ -211,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
@@ -240,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";
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());
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]);
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++;
/*
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);
}
void Kernel::PrintInt(int value)
{
return synchConsoleOut->PutInt(value);
}

View File

@@ -25,31 +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
// from constructor because
// refers to "kernel" as a global
void ExecAll();
int Exec(char* name);
// 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];}
Thread* getThread(int threadID){return t[threadID];}
void PrintInt(int n);
int CreateFile(char* filename); // fileSystem call
// These are public for notational convenience; really,
// they're global variables used everywhere.
// These are public for notational convenience; really,
// they're global variables used everywhere.
Thread *currentThread; // the thread holding the CPU
Scheduler *scheduler; // the ready list
@@ -60,17 +63,21 @@ class Kernel {
SynchConsoleInput *synchConsoleIn;
SynchConsoleOutput *synchConsoleOut;
SynchDisk *synchDisk;
FileSystem *fileSystem;
FileSystem *fileSystem;
PostOfficeInput *postOfficeIn;
PostOfficeOutput *postOfficeOut;
FrameTable *frameTable;
int hostName; // machine identifier
private:
Thread* t[10];
// Todo ----
char* execfile[10];
int execPriority[10];
// ---------
int execfileNum;
int threadNum;
bool randomSlice; // enable pseudo-random time slicing
@@ -85,5 +92,3 @@ class Kernel {
#endif // KERNEL_H

View File

@@ -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();
}

View File

@@ -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,14 +85,16 @@ Scheduler::ReadyToRun (Thread *thread)
// Thread is removed from the ready list.
//----------------------------------------------------------------------
Thread *
Scheduler::FindNextToRun ()
Thread*
Scheduler::FindNextToRun()
{
ASSERT(kernel->interrupt->getLevel() == IntOff);
if (readyList->IsEmpty()) {
return NULL;
} else {
}
else {
DEBUG(dbgSche, "[B] Tick [" << kernel->stats->totalTicks << "]: Process [" << readyList->Front()->getName() << "] is removed from queue.");
return readyList->RemoveFront();
}
}
@@ -101,9 +117,9 @@ Scheduler::FindNextToRun ()
//----------------------------------------------------------------------
void
Scheduler::Run (Thread *nextThread, bool finishing)
Scheduler::Run(Thread* nextThread, bool finishing)
{
Thread *oldThread = kernel->currentThread;
Thread* oldThread = kernel->currentThread;
ASSERT(kernel->interrupt->getLevel() == IntOff);
@@ -124,6 +140,7 @@ Scheduler::Run (Thread *nextThread, bool finishing)
nextThread->setStatus(RUNNING); // nextThread is now running
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
@@ -165,7 +182,7 @@ Scheduler::CheckToBeDestroyed()
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);
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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,19 +33,20 @@ 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;
name = threadName;
stackTop = NULL;
stack = NULL;
status = JUST_CREATED;
for (int i = 0; i < MachineStateSize; i++) {
machineState[i] = NULL; // not strictly necessary, since
// new thread ignores contents
// of machine registers
}
space = NULL;
ID = threadID;
name = threadName;
stackTop = NULL;
stack = NULL;
status = JUST_CREATED;
for (int i = 0; i < MachineStateSize; i++)
{
machineState[i] = NULL; // not strictly necessary, since
// new thread ignores contents
// of machine registers
}
space = NULL;
}
//----------------------------------------------------------------------
@@ -62,15 +63,15 @@ Thread::Thread(char* threadName, int threadID)
Thread::~Thread()
{
DEBUG(dbgThread, "Deleting thread: " << name);
ASSERT(this != kernel->currentThread);
if (stack != NULL)
DeallocBoundedArray((char *) stack, StackSize * sizeof(int));
DEBUG(dbgThread, "Deleting thread: " << name);
ASSERT(this != kernel->currentThread);
if (stack != NULL)
DeallocBoundedArray((char *)stack, StackSize * sizeof(int));
}
//----------------------------------------------------------------------
// Thread::Fork
// Invoke (*func)(arg), allowing caller and callee to execute
// 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;
Interrupt *interrupt = kernel->interrupt;
Scheduler *scheduler = kernel->scheduler;
IntStatus oldLevel;
DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int) func << " " << arg);
StackAllocate(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);
}
oldLevel = interrupt->SetLevel(IntOff);
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);
ASSERT(this == kernel->currentThread);
(void)kernel->interrupt->SetLevel(IntOff);
ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Finishing thread: " << name);
Sleep(TRUE); // invokes SWITCH
// not reached
DEBUG(dbgThread, "Finishing thread: " << name);
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);
Thread *nextThread;
IntStatus oldLevel = kernel->interrupt->SetLevel(IntOff);
ASSERT(this == kernel->currentThread);
ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Yielding thread: " << name);
DEBUG(dbgThread, "Yielding thread: " << name);
nextThread = kernel->scheduler->FindNextToRun();
if (nextThread != NULL) {
kernel->scheduler->ReadyToRun(this);
kernel->scheduler->Run(nextThread, FALSE);
}
(void) kernel->interrupt->SetLevel(oldLevel);
nextThread = kernel->scheduler->FindNextToRun();
if (nextThread != NULL)
{
kernel->scheduler->ReadyToRun(this);
kernel->scheduler->Run(nextThread, FALSE);
}
(void)kernel->interrupt->SetLevel(oldLevel);
}
//----------------------------------------------------------------------
// Thread::Sleep
// Relinquish the CPU, because the current thread has either
// finished or is blocked waiting on a synchronization
// 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,34 +228,34 @@ 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;
Thread *nextThread;
ASSERT(this == kernel->currentThread);
ASSERT(kernel->interrupt->getLevel() == IntOff);
ASSERT(this == kernel->currentThread);
ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Sleeping thread: " << name);
DEBUG(dbgThread, "Sleeping thread: " << name);
status = BLOCKED;
//cout << "debug Thread::Sleep " << name << "wait for Idle\n";
while ((nextThread = kernel->scheduler->FindNextToRun()) == NULL) {
kernel->interrupt->Idle(); // no one to run, wait for an interrupt
}
// returns when it's time for us to run
kernel->scheduler->Run(nextThread, finishing);
status = BLOCKED;
// cout << "debug Thread::Sleep " << name << "wait for Idle\n";
while ((nextThread = kernel->scheduler->FindNextToRun()) == NULL)
{
kernel->interrupt->Idle(); // no one to run, wait for an interrupt
}
// returns when it's time for us to run
kernel->scheduler->Run(nextThread, finishing);
}
//----------------------------------------------------------------------
// ThreadBegin, ThreadFinish, ThreadPrint
// Dummy functions because C++ does not (easily) allow pointers to member
// functions. So we create a dummy C function
// (which we can pass a pointer to), that then simply calls the
// (which we can pass a pointer to), that then simply calls the
// member function.
//----------------------------------------------------------------------
@@ -277,16 +274,19 @@ void ThreadPrint(Thread *t) { t->Print(); }
static void *
PLabelToAddr(void *plabel)
{
int funcPtr = (int) plabel;
int funcPtr = (int)plabel;
if (funcPtr & 0x02) {
// L-Field is set. This is a PLT pointer
funcPtr -= 2; // Get rid of the L bit
return (*(void **)funcPtr);
} else {
// L-field not set.
return plabel;
}
if (funcPtr & 0x02)
{
// L-Field is set. This is a PLT pointer
funcPtr -= 2; // Get rid of the L bit
return (*(void **)funcPtr);
}
else
{
// L-field not set.
return plabel;
}
}
#endif
@@ -302,62 +302,60 @@ 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
stack[StackSize - 1] = STACK_FENCEPOST;
// HP stack works from low addresses to high addresses
// everyone else works the other way: from high addresses to low addresses
stackTop = stack + 16; // HP requires 64-byte frame marker
stack[StackSize - 1] = STACK_FENCEPOST;
#endif
#ifdef SPARC
stackTop = stack + StackSize - 96; // SPARC stack must contains at
// least 1 activation record
// to start with.
*stack = STACK_FENCEPOST;
#endif
stackTop = stack + StackSize - 96; // SPARC stack must contains at
// least 1 activation record
// to start with.
*stack = STACK_FENCEPOST;
#endif
#ifdef PowerPC // RS6000
stackTop = stack + StackSize - 16; // RS6000 requires 64-byte frame marker
*stack = STACK_FENCEPOST;
#endif
#ifdef PowerPC // RS6000
stackTop = stack + StackSize - 16; // RS6000 requires 64-byte frame marker
*stack = STACK_FENCEPOST;
#endif
#ifdef DECMIPS
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
*stack = STACK_FENCEPOST;
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
*stack = STACK_FENCEPOST;
#endif
#ifdef ALPHA
stackTop = stack + StackSize - 8; // -8 to be on the safe side!
*stack = STACK_FENCEPOST;
stackTop = stack + StackSize - 8; // -8 to be on the safe side!
*stack = STACK_FENCEPOST;
#endif
#ifdef x86
// the x86 passes the return address on the stack. In order for SWITCH()
// to go to ThreadRoot when we switch to this thread, the return addres
// used in SWITCH() must be the starting address of ThreadRoot.
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
*(--stackTop) = (int) ThreadRoot;
*stack = STACK_FENCEPOST;
// the x86 passes the return address on the stack. In order for SWITCH()
// to go to ThreadRoot when we switch to this thread, the return addres
// used in SWITCH() must be the starting address of ThreadRoot.
stackTop = stack + StackSize - 4; // -4 to be on the safe side!
*(--stackTop) = (int)ThreadRoot;
*stack = STACK_FENCEPOST;
#endif
#ifdef PARISC
machineState[PCState] = PLabelToAddr(ThreadRoot);
machineState[StartupPCState] = PLabelToAddr(ThreadBegin);
machineState[InitialPCState] = PLabelToAddr(func);
machineState[InitialArgState] = arg;
machineState[WhenDonePCState] = PLabelToAddr(ThreadFinish);
machineState[PCState] = PLabelToAddr(ThreadRoot);
machineState[StartupPCState] = PLabelToAddr(ThreadBegin);
machineState[InitialPCState] = PLabelToAddr(func);
machineState[InitialArgState] = arg;
machineState[WhenDonePCState] = PLabelToAddr(ThreadFinish);
#else
machineState[PCState] = (void*)ThreadRoot;
machineState[StartupPCState] = (void*)ThreadBegin;
machineState[InitialPCState] = (void*)func;
machineState[InitialArgState] = (void*)arg;
machineState[WhenDonePCState] = (void*)ThreadFinish;
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]);
for (int i = 0; i < NumTotalRegs; 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;
}
// ---------

View File

@@ -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