diff --git a/code/build.linux/Makefile b/code/build.linux/Makefile index 54fcf99..544a062 100644 --- a/code/build.linux/Makefile +++ b/code/build.linux/Makefile @@ -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 -Wextra $(INCPATH) $(DEFINES) $(HOSTCFLAGS) -DCHANGED -m32 # -fsanitize=address,undefined -LDFLAGS = -m32 # -fsanitize=address,undefined +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 @@ -332,7 +332,6 @@ S_OFILES = switch.o OFILES = $(C_OFILES) $(S_OFILES) $(PROGRAM): $(OFILES) - cat ../test/*.sh ../test/Makefile ../test/*.c $(LD) $(OFILES) $(LDFLAGS) -o $(PROGRAM) $(C_OFILES): %.o: diff --git a/code/filesys/filesys.h b/code/filesys/filesys.h index b38b08c..e8c6a0a 100644 --- a/code/filesys/filesys.h +++ b/code/filesys/filesys.h @@ -40,56 +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); + bool Create(char* name) { + int fileDescriptor = OpenForWrite(name); + if (fileDescriptor == -1) return FALSE; + Close(fileDescriptor); + return TRUE; + } - if (fileDescriptor == -1) return FALSE; - Close(fileDescriptor); - return TRUE; - } + OpenFile* Open(char* name) { + int fileDescriptor = OpenForReadWrite(name, FALSE); + if (fileDescriptor == -1) return NULL; + return new OpenFile(fileDescriptor); + } - OpenFile* Open(char *name) { - int fileDescriptor = OpenForReadWrite(name, FALSE); + OpenFileId OpenFiles(char* name) { + OpenFile* file = Open(name); + if (!file) return -1; + int freeIndex = -1; - if (fileDescriptor == -1) return NULL; - return new OpenFile(fileDescriptor); - } + for (int i = 0; i < 20; i++) + if (!fileDescriptorTable[i]) + freeIndex = i; - bool Remove(char *name) { return Unlink(name) == 0; } + 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 diff --git a/code/filesys/openfile.h b/code/filesys/openfile.h index af72c5c..de0f260 100644 --- a/code/filesys/openfile.h +++ b/code/filesys/openfile.h @@ -27,32 +27,38 @@ #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 ~OpenFile() { Close(file); } // close the file int ReadAt(char *into, int numBytes, int position) { - Lseek(file, position, 0); - return ReadPartial(file, into, numBytes); + Lseek(file, position, 0); + return ReadPartial(file, into, numBytes); } int WriteAt(char *from, int numBytes, int position) { - Lseek(file, position, 0); - WriteFile(file, from, numBytes); - return numBytes; + Lseek(file, position, 0); + WriteFile(file, from, numBytes); + return numBytes; } int Read(char *into, int numBytes) { - int numRead = ReadAt(into, numBytes, currentOffset); - currentOffset += numRead; - return numRead; - } + int numRead = ReadAt(into, numBytes, currentOffset); + currentOffset += numRead; + return numRead; + } int Write(char *from, int numBytes) { - int numWritten = WriteAt(from, numBytes, currentOffset); - currentOffset += numWritten; - return numWritten; + int numWritten = WriteAt(from, numBytes, currentOffset); + currentOffset += numWritten; + return numWritten; } int Length() { Lseek(file, 0, 2); return Tell(file); } + + OpenFileId GetFileDescriptor() { + return file; + } private: int file; diff --git a/code/lib/debug.h b/code/lib/debug.h index cb5eba2..b7daf50 100644 --- a/code/lib/debug.h +++ b/code/lib/debug.h @@ -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: diff --git a/code/lib/sysdep.cc b/code/lib/sysdep.cc index c6f6dbd..a83c8da 100644 --- a/code/lib/sysdep.cc +++ b/code/lib/sysdep.cc @@ -308,7 +308,6 @@ int OpenForWrite(char *name) { int fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0666); - // cerr << "OpenForWrite name, fd: " << (int)name << ", " << fd << endl; ASSERT(fd >= 0); return fd; @@ -326,7 +325,6 @@ int OpenForReadWrite(char *name, bool crashOnError) { int fd = open(name, O_RDWR, 0); - // cerr << "OpenForReadWrite name, fd: " << (int)name << ", " << fd << endl; ASSERT(!crashOnError || fd >= 0); return fd; diff --git a/code/machine/console.cc b/code/machine/console.cc index d9d3d33..951398a 100644 --- a/code/machine/console.cc +++ b/code/machine/console.cc @@ -172,13 +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); + char * printStr = (char*)malloc(sizeof(char) * 15); sprintf(printStr, "%d\n", value); - WriteFile(writeFileNo, printStr, strlen(printStr)*sizeof(char)); + WriteFile(writeFileNo, printStr, strlen(printStr) * sizeof(char)); putBusy = TRUE; kernel->interrupt->Schedule(this, ConsoleTime, ConsoleWriteInt); } diff --git a/code/machine/console.h b/code/machine/console.h index 2047594..1d82c92 100644 --- a/code/machine/console.h +++ b/code/machine/console.h @@ -76,7 +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 n); + + void PutInt(int value); + void CallBack(); // Invoked when next character can be put // out to the display. diff --git a/code/machine/interrupt.cc b/code/machine/interrupt.cc index 9a1e77b..b09d17e 100644 --- a/code/machine/interrupt.cc +++ b/code/machine/interrupt.cc @@ -22,8 +22,8 @@ #include "copyright.h" #include "interrupt.h" -#include "main.h" #include "synchconsole.h" +#include "main.h" // String definitions for debugging messages @@ -341,7 +341,7 @@ static void PrintPending (PendingInterrupt *pending) { cout << "Interrupt handler "<< intTypeNames[pending->type]; - cout << ", scheduled at " << pending->when << endl; + cout << ", scheduled at " << pending->when; } //---------------------------------------------------------------------- @@ -360,8 +360,31 @@ Interrupt::DumpState() cout << "\nEnd of pending interrupts\n"; } -void -Interrupt::PrintInt(int value) +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); +} diff --git a/code/machine/interrupt.h b/code/machine/interrupt.h index 7108b05..7b3f4f6 100644 --- a/code/machine/interrupt.h +++ b/code/machine/interrupt.h @@ -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 *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* 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 diff --git a/code/machine/machine.cc b/code/machine/machine.cc index 9aa234c..429d4f7 100644 --- a/code/machine/machine.cc +++ b/code/machine/machine.cc @@ -13,17 +13,10 @@ // Textual names of the exceptions that can be generated by user program // execution, for debugging. -static char* exceptionNames[] = { - "no exception", - "syscall", - "page fault/no TLB entry", - "page read only", - "bus error", - "address error", - "overflow", - "illegal instruction", - "bad memory allocation" -}; +static char* exceptionNames[] = { "no exception", "syscall", + "page fault/no TLB entry", "page read only", + "bus error", "address error", "overflow", + "illegal instruction" }; //---------------------------------------------------------------------- // CheckEndian diff --git a/code/machine/machine.h b/code/machine/machine.h index e601e14..bc5f1d6 100644 --- a/code/machine/machine.h +++ b/code/machine/machine.h @@ -28,34 +28,34 @@ // Definitions related to the size, and format of user memory const int PageSize = 128; // set the page size equal to - // the disk sector size, for simplicity + // the disk sector size, for simplicity - // - // You are allowed to change this value. - // Doing so will change the number of pages of physical memory - // available on the simulated machine. - // +// +// You are allowed to change this value. +// Doing so will change the number of pages of physical memory +// available on the simulated machine. +// const int NumPhysPages = 128; const int MemorySize = (NumPhysPages * PageSize); const int TLBSize = 4; // if there is a TLB, make it small -enum ExceptionType { - NoException, // Everything ok! - SyscallException, // A program executed a system call. - PageFaultException, // No valid translation found - ReadOnlyException, // Write attempted to page marked - // "read-only" - BusErrorException, // Translation resulted in an - // invalid physical address - AddressErrorException, // Unaligned reference or one that - // was beyond the end of the - // address space - OverflowException, // Integer overflow in add or sub. - IllegalInstrException, // Unimplemented or reserved instr. - MemoryLimitException, // Bad allocation +enum ExceptionType { NoException, // Everything ok! + SyscallException, // A program executed a system call. + PageFaultException, // No valid translation found + ReadOnlyException, // Write attempted to page marked + // "read-only" + BusErrorException, // Translation resulted in an + // invalid physical address + AddressErrorException, // Unaligned reference or one that + // was beyond the end of the + // address space + OverflowException, // Integer overflow in add or sub. + IllegalInstrException, // Unimplemented or reserved instr. + + MemoryLimitException, // Insufficient memory - NumExceptionTypes + NumExceptionTypes }; // User program CPU state. The full set of MIPS registers, plus a few @@ -96,97 +96,97 @@ class Interrupt; class Machine { public: Machine(bool debug); // Initialize the simulation of the hardware - // for running user programs + // for running user programs ~Machine(); // De-allocate the data structures - // Routines callable by the Nachos kernel +// Routines callable by the Nachos kernel void Run(); // Run a user program int ReadRegister(int num); // read the contents of a CPU register void WriteRegister(int num, int value); - // store a value into a CPU register + // store a value into a CPU register - // Data structures accessible to the Nachos kernel -- main memory and the - // page table/TLB. - // - // Note that *all* communication between the user program and the kernel - // are in terms of these data structures (plus the CPU registers). +// Data structures accessible to the Nachos kernel -- main memory and the +// page table/TLB. +// +// Note that *all* communication between the user program and the kernel +// are in terms of these data structures (plus the CPU registers). char *mainMemory; // physical memory to store user program, - // code and data, while executing + // code and data, while executing - // NOTE: the hardware translation of virtual addresses in the user program - // to physical addresses (relative to the beginning of "mainMemory") - // can be controlled by one of: - // a traditional linear page table - // a software-loaded translation lookaside buffer (tlb) -- a cache of - // mappings of virtual page #'s to physical page #'s - // - // If "tlb" is NULL, the linear page table is used - // If "tlb" is non-NULL, the Nachos kernel is responsible for managing - // the contents of the TLB. But the kernel can use any data structure - // it wants (eg, segmented paging) for handling TLB cache misses. - // - // For simplicity, both the page table pointer and the TLB pointer are - // public. However, while there can be multiple page tables (one per address - // space, stored in memory), there is only one TLB (implemented in hardware). - // Thus the TLB pointer should be considered as *read-only*, although - // the contents of the TLB are free to be modified by the kernel software. +// NOTE: the hardware translation of virtual addresses in the user program +// to physical addresses (relative to the beginning of "mainMemory") +// can be controlled by one of: +// a traditional linear page table +// a software-loaded translation lookaside buffer (tlb) -- a cache of +// mappings of virtual page #'s to physical page #'s +// +// If "tlb" is NULL, the linear page table is used +// If "tlb" is non-NULL, the Nachos kernel is responsible for managing +// the contents of the TLB. But the kernel can use any data structure +// it wants (eg, segmented paging) for handling TLB cache misses. +// +// For simplicity, both the page table pointer and the TLB pointer are +// public. However, while there can be multiple page tables (one per address +// space, stored in memory), there is only one TLB (implemented in hardware). +// Thus the TLB pointer should be considered as *read-only*, although +// the contents of the TLB are free to be modified by the kernel software. TranslationEntry *tlb; // this pointer should be considered - // "read-only" to Nachos kernel code + // "read-only" to Nachos kernel code TranslationEntry *pageTable; unsigned int pageTableSize; bool ReadMem(int addr, int size, int* value); bool WriteMem(int addr, int size, int value); - // Read or write 1, 2, or 4 bytes of virtual - // memory (at addr). Return FALSE if a - // correct translation couldn't be found. + // Read or write 1, 2, or 4 bytes of virtual + // memory (at addr). Return FALSE if a + // correct translation couldn't be found. private: - // Routines internal to the machine simulation -- DO NOT call these directly +// Routines internal to the machine simulation -- DO NOT call these directly void DelayedLoad(int nextReg, int nextVal); - // Do a pending delayed load (modifying a reg) + // Do a pending delayed load (modifying a reg) void OneInstruction(Instruction *instr); - // Run one instruction of a user program. - + // Run one instruction of a user program. + ExceptionType Translate(int virtAddr, int* physAddr, int size,bool writing); - // Translate an address, and check for - // alignment. Set the use and dirty bits in - // the translation entry appropriately, - // and return an exception code if the - // translation couldn't be completed. + // Translate an address, and check for + // alignment. Set the use and dirty bits in + // the translation entry appropriately, + // and return an exception code if the + // translation couldn't be completed. void RaiseException(ExceptionType which, int badVAddr); - // Trap to the Nachos kernel, because of a - // system call or other exception. + // Trap to the Nachos kernel, because of a + // system call or other exception. void Debugger(); // invoke the user program debugger void DumpState(); // print the user CPU and memory state - // Internal data structures +// Internal data structures int registers[NumTotalRegs]; // CPU registers, for executing user programs bool singleStep; // drop back into the debugger after each - // simulated instruction + // simulated instruction int runUntilTime; // drop back into the debugger when simulated - // time reaches this value + // time reaches this value friend class Interrupt; // calls DelayedLoad() }; extern void ExceptionHandler(ExceptionType which); -// Entry point into Nachos for handling -// user system calls and exceptions -// Defined in exception.cc + // Entry point into Nachos for handling + // user system calls and exceptions + // Defined in exception.cc // Routines for converting Words and Short Words to and from the diff --git a/code/machine/stats.h b/code/machine/stats.h index 946c3b9..48ea46f 100644 --- a/code/machine/stats.h +++ b/code/machine/stats.h @@ -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 diff --git a/code/machine/translate.cc b/code/machine/translate.cc index 7b92fe6..969203b 100644 --- a/code/machine/translate.cc +++ b/code/machine/translate.cc @@ -85,38 +85,38 @@ ShortToMachine(unsigned short shortword) { return ShortToHost(shortword); } bool Machine::ReadMem(int addr, int size, int *value) { - int data; - ExceptionType exception; - int physicalAddress; + int data; + ExceptionType exception; + int physicalAddress; + + DEBUG(dbgAddr, "Reading VA " << addr << ", size " << size); + + exception = Translate(addr, &physicalAddress, size, FALSE); + if (exception != NoException) { + RaiseException(exception, addr); + return FALSE; + } + switch (size) { + case 1: + data = mainMemory[physicalAddress]; + *value = data; + break; + + case 2: + data = *(unsigned short *) &mainMemory[physicalAddress]; + *value = ShortToHost(data); + break; + + case 4: + data = *(unsigned int *) &mainMemory[physicalAddress]; + *value = WordToHost(data); + break; - DEBUG(dbgAddr, "Reading VA " << addr << ", size " << size); - - exception = Translate(addr, &physicalAddress, size, FALSE); - if (exception != NoException) { - RaiseException(exception, addr); - return FALSE; - } - switch (size) { - case 1: - data = mainMemory[physicalAddress]; - *value = data; - break; - - case 2: - data = *(unsigned short *) &mainMemory[physicalAddress]; - *value = ShortToHost(data); - break; - - case 4: - data = *(unsigned int *) &mainMemory[physicalAddress]; - *value = WordToHost(data); - break; - - default: ASSERT(FALSE); - } - - DEBUG(dbgAddr, "\tvalue read = " << *value); - return (TRUE); + default: ASSERT(FALSE); + } + + DEBUG(dbgAddr, "\tvalue read = " << *value); + return (TRUE); } //---------------------------------------------------------------------- @@ -135,35 +135,35 @@ Machine::ReadMem(int addr, int size, int *value) bool Machine::WriteMem(int addr, int size, int value) { - ExceptionType exception; - int physicalAddress; + ExceptionType exception; + int physicalAddress; + + DEBUG(dbgAddr, "Writing VA " << addr << ", size " << size << ", value " << value); - DEBUG(dbgAddr, "Writing VA " << addr << ", size " << size << ", value " << value); + exception = Translate(addr, &physicalAddress, size, TRUE); + if (exception != NoException) { + RaiseException(exception, addr); + return FALSE; + } + switch (size) { + case 1: + mainMemory[physicalAddress] = (unsigned char) (value & 0xff); + break; - exception = Translate(addr, &physicalAddress, size, TRUE); - if (exception != NoException) { - RaiseException(exception, addr); - return FALSE; - } - switch (size) { - case 1: - mainMemory[physicalAddress] = (unsigned char) (value & 0xff); - break; - - case 2: - *(unsigned short *) &mainMemory[physicalAddress] - = ShortToMachine((unsigned short) (value & 0xffff)); - break; - - case 4: - *(unsigned int *) &mainMemory[physicalAddress] - = WordToMachine((unsigned int) value); - break; - - default: ASSERT(FALSE); - } - - return TRUE; + case 2: + *(unsigned short *) &mainMemory[physicalAddress] + = ShortToMachine((unsigned short) (value & 0xffff)); + break; + + case 4: + *(unsigned int *) &mainMemory[physicalAddress] + = WordToMachine((unsigned int) value); + break; + + default: ASSERT(FALSE); + } + + return TRUE; } //---------------------------------------------------------------------- @@ -184,75 +184,67 @@ Machine::WriteMem(int addr, int size, int value) ExceptionType Machine::Translate(int virtAddr, int* physAddr, int size, bool writing) { - int i; - unsigned int vpn, offset; - TranslationEntry *entry; - unsigned int pageFrame; + int i; + unsigned int vpn, offset; + TranslationEntry *entry; + unsigned int pageFrame; - DEBUG(dbgAddr, "\tTranslate " << virtAddr << (writing ? " , write" : " , read")); + DEBUG(dbgAddr, "\tTranslate " << virtAddr << (writing ? " , write" : " , read")); - // check for alignment errors - if (((size == 4) && (virtAddr & 0x3)) || ((size == 2) && (virtAddr & 0x1))){ - DEBUG(dbgAddr, "Alignment problem at " << virtAddr << ", size " << size); - return AddressErrorException; - } - // we must have either a TLB or a page table, but not both! - ASSERT(tlb == NULL || pageTable == NULL); - ASSERT(tlb != NULL || pageTable != NULL); - - // calculate the virtual page number, and offset within the page, - // from the virtual address - vpn = (unsigned) virtAddr / PageSize; - offset = (unsigned) virtAddr % PageSize; - - if (tlb == NULL) { // => page table => vpn is index into table - if (vpn >= pageTableSize) { - DEBUG(dbgAddr, "Illegal virtual page # " << virtAddr); - DEBUG(dbgAddr, "vpn, pageTableSize, NumPhysPages: " << vpn << ' ' << pageTableSize << ' ' << NumPhysPages); - return AddressErrorException; - } else if (!pageTable[vpn].valid) { - DEBUG(dbgAddr, "Invalid virtual page # " << virtAddr); - return PageFaultException; +// check for alignment errors + if (((size == 4) && (virtAddr & 0x3)) || ((size == 2) && (virtAddr & 0x1))){ + DEBUG(dbgAddr, "Alignment problem at " << virtAddr << ", size " << size); + return AddressErrorException; } - entry = &pageTable[vpn]; - } else { - for (entry = NULL, i = 0; i < TLBSize; i++) - if (tlb[i].valid && (tlb[i].virtualPage == ((int)vpn))) { - entry = &tlb[i]; // FOUND! - break; - } - if (entry == NULL) { // not found - DEBUG(dbgAddr, "Invalid TLB entry for this virtual page!"); - return PageFaultException; // really, this is a TLB fault, - // the page may be in memory, - // but not in the TLB - } - } + // we must have either a TLB or a page table, but not both! + ASSERT(tlb == NULL || pageTable == NULL); + ASSERT(tlb != NULL || pageTable != NULL); - if (entry->readOnly && writing) { // trying to write to a read-only page - DEBUG(dbgAddr, "Write to read-only page at " << virtAddr); - return ReadOnlyException; - } - pageFrame = entry->physicalPage; - if (pageFrame == -1) { - pageFrame = entry->physicalPage = kernel->frameTable->Allocate(); - if (pageFrame == -1) { - DEBUG(dbgAddr, "Memory Limit exceeded"); - return MemoryLimitException; +// calculate the virtual page number, and offset within the page, +// from the virtual address + vpn = (unsigned) virtAddr / PageSize; + offset = (unsigned) virtAddr % PageSize; + + if (tlb == NULL) { // => page table => vpn is index into table + if (vpn >= pageTableSize) { + DEBUG(dbgAddr, "Illegal virtual page # " << virtAddr); + return AddressErrorException; + } else if (!pageTable[vpn].valid) { + DEBUG(dbgAddr, "Invalid virtual page # " << virtAddr); + return PageFaultException; + } + entry = &pageTable[vpn]; + } else { + for (entry = NULL, i = 0; i < TLBSize; i++) + if (tlb[i].valid && (tlb[i].virtualPage == ((int)vpn))) { + entry = &tlb[i]; // FOUND! + break; + } + if (entry == NULL) { // not found + DEBUG(dbgAddr, "Invalid TLB entry for this virtual page!"); + return PageFaultException; // really, this is a TLB fault, + // the page may be in memory, + // but not in the TLB + } } - } - // if the pageFrame is too big, there is something really wrong! - // An invalid translation was loaded into the page table or TLB. - if (pageFrame >= NumPhysPages) { - DEBUG(dbgAddr, "Illegal pageframe " << pageFrame); - return BusErrorException; - } - entry->use = TRUE; // set the use, dirty bits - if (writing) - entry->dirty = TRUE; - *physAddr = pageFrame * PageSize + offset; - ASSERT((*physAddr >= 0) && ((*physAddr + size) <= MemorySize)); - DEBUG(dbgAddr, "phys addr = " << *physAddr); - return NoException; + if (entry->readOnly && writing) { // trying to write to a read-only page + DEBUG(dbgAddr, "Write to read-only page at " << virtAddr); + return ReadOnlyException; + } + pageFrame = entry->physicalPage; + + // if the pageFrame is too big, there is something really wrong! + // An invalid translation was loaded into the page table or TLB. + if (pageFrame >= NumPhysPages) { + DEBUG(dbgAddr, "Illegal pageframe " << pageFrame); + return BusErrorException; + } + entry->use = TRUE; // set the use, dirty bits + if (writing) + entry->dirty = TRUE; + *physAddr = pageFrame * PageSize + offset; + ASSERT((*physAddr >= 0) && ((*physAddr + size) <= MemorySize)); + DEBUG(dbgAddr, "phys addr = " << *physAddr); + return NoException; } diff --git a/code/test/Makefile b/code/test/Makefile index 3ca2e99..b1fb223 100644 --- a/code/test/Makefile +++ b/code/test/Makefile @@ -113,8 +113,7 @@ 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 test -# PROGRAMS = halt +PROGRAMS = mp4_consoleIO_1 mp4_consoleIO_2 mp4_consoleIO_3 mp4_consoleIO_4 endif all: $(PROGRAMS) @@ -122,78 +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 - -test.o: test.c - $(CC) $(CFLAGS) -c test.c -test: test.o start.o - $(LD) $(LDFLAGS) start.o test.o -o test.coff - $(COFF2NOFF) test.coff test clean: $(RM) -f *.o *.ii @@ -203,12 +159,11 @@ distclean: clean $(RM) -f $(PROGRAMS) run: $(PROGRAMS) - timeout 1 $(NACHOS) -e consoleIO_test1 -e consoleIO_test2 + 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) -e consoleIO_test1 -e consoleIO_test2 -d + - + timeout 1 $(NACHOS) -d z -ep mp4_consoleIO_1 60 -ep mp4_consoleIO_2 70 unknownhost: @echo Host type could not be determined. diff --git a/code/test/mp4_consoleIO_1.c b/code/test/mp4_consoleIO_1.c new file mode 100644 index 0000000..7f64e4e --- /dev/null +++ b/code/test/mp4_consoleIO_1.c @@ -0,0 +1,10 @@ +#include "syscall.h" + +int main() +{ + int n; + for (n = 0; n < 4; n++) { + PrintInt(1); + } + return 0; +} diff --git a/code/test/mp4_consoleIO_2.c b/code/test/mp4_consoleIO_2.c new file mode 100644 index 0000000..3efa141 --- /dev/null +++ b/code/test/mp4_consoleIO_2.c @@ -0,0 +1,9 @@ +#include "syscall.h" +int main() +{ + int n; + for (n = 0; n < 5; n++) { + PrintInt(2); + } + return 0; +} diff --git a/code/test/mp4_consoleIO_3.c b/code/test/mp4_consoleIO_3.c new file mode 100644 index 0000000..1e265c8 --- /dev/null +++ b/code/test/mp4_consoleIO_3.c @@ -0,0 +1,10 @@ +#include "syscall.h" +int +main() +{ + int n; + for (n = 0; n < 12; n++) { + PrintInt(3); + } + return 0; +} diff --git a/code/test/mp4_consoleIO_4.c b/code/test/mp4_consoleIO_4.c new file mode 100644 index 0000000..888191d --- /dev/null +++ b/code/test/mp4_consoleIO_4.c @@ -0,0 +1,9 @@ +#include "syscall.h" +int main() +{ + int n; + for (n = 0; n < 11; n++) { + PrintInt(4); + } + return 0; +} diff --git a/code/test/os_students.sh b/code/test/os_students.sh index 9f7eada..2b3b450 100644 --- a/code/test/os_students.sh +++ b/code/test/os_students.sh @@ -1,4 +1,9 @@ make distclean -make -timeout 1 ../build.linux/nachos -e consoleIO_test1 -e consoleIO_test2 -echo 'done' +make +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" \ No newline at end of file diff --git a/code/test/start.S b/code/test/start.S index 0e7a7a7..2acb37a 100644 --- a/code/test/start.S +++ b/code/test/start.S @@ -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: @@ -186,14 +194,6 @@ ThreadJoin: j $31 .end ThreadJoin - .globl PrintInt - .ent PrintInt -PrintInt: - addiu $2,$0,SC_PrintInt - syscall - j $31 - .end PrintInt - /* dummy function to keep gcc happy */ .globl __main diff --git a/code/threads/alarm.cc b/code/threads/alarm.cc index 4cd1248..f230fb7 100644 --- a/code/threads/alarm.cc +++ b/code/threads/alarm.cc @@ -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(); } + // --------- } diff --git a/code/threads/kernel.cc b/code/threads/kernel.cc index 81000b1..00f0af4 100644 --- a/code/threads/kernel.cc +++ b/code/threads/kernel.cc @@ -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 *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; - synchList->SelfTest(9); - delete synchList; + Semaphore* semaphore; + SynchList* 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; + 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); -} + diff --git a/code/threads/kernel.h b/code/threads/kernel.h index 51d3dee..78d1e51 100644 --- a/code/threads/kernel.h +++ b/code/threads/kernel.h @@ -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 - - diff --git a/code/threads/main.cc b/code/threads/main.cc index 093b7d3..ead7707 100644 --- a/code/threads/main.cc +++ b/code/threads/main.cc @@ -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(); } diff --git a/code/threads/scheduler.cc b/code/threads/scheduler.cc index 11278b9..6c305b6 100644 --- a/code/threads/scheduler.cc +++ b/code/threads/scheduler.cc @@ -22,6 +22,7 @@ #include "debug.h" #include "scheduler.h" #include "main.h" +#include //---------------------------------------------------------------------- // 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; - 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); } diff --git a/code/threads/scheduler.h b/code/threads/scheduler.h index 36964d6..c78cc18 100644 --- a/code/threads/scheduler.h +++ b/code/threads/scheduler.h @@ -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 *readyList; // queue of threads that are ready to run, - // but not running + // Todo ---- + SortedList *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 diff --git a/code/threads/synch.cc b/code/threads/synch.cc index 5be111c..f62f2db 100644 --- a/code/threads/synch.cc +++ b/code/threads/synch.cc @@ -48,7 +48,7 @@ Semaphore::Semaphore(char* debugName, int initialValue) { name = debugName; value = initialValue; - queue = new List; + queue = new List; } //---------------------------------------------------------------------- @@ -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; + waitQueue = new List; } //---------------------------------------------------------------------- @@ -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); diff --git a/code/threads/thread.cc b/code/threads/thread.cc index 8bae3ad..87a24f1 100644 --- a/code/threads/thread.cc +++ b/code/threads/thread.cc @@ -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; +} +// --------- diff --git a/code/threads/thread.h b/code/threads/thread.h index e311430..38f3a30 100644 --- a/code/threads/thread.h +++ b/code/threads/thread.h @@ -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 diff --git a/code/userprog/addrspace.cc b/code/userprog/addrspace.cc index 451a23b..f71afee 100644 --- a/code/userprog/addrspace.cc +++ b/code/userprog/addrspace.cc @@ -27,101 +27,35 @@ // 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); - noffH->code.virtualAddr = WordToHost(noffH->code.virtualAddr); - noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr); + noffH->noffMagic = WordToHost(noffH->noffMagic); + noffH->code.size = WordToHost(noffH->code.size); + noffH->code.virtualAddr = WordToHost(noffH->code.virtualAddr); + noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr); #ifdef RDATA - noffH->readonlyData.size = WordToHost(noffH->readonlyData.size); - noffH->readonlyData.virtualAddr = - WordToHost(noffH->readonlyData.virtualAddr); - noffH->readonlyData.inFileAddr = - WordToHost(noffH->readonlyData.inFileAddr); + noffH->readonlyData.size = WordToHost(noffH->readonlyData.size); + noffH->readonlyData.virtualAddr = + WordToHost(noffH->readonlyData.virtualAddr); + noffH->readonlyData.inFileAddr = + WordToHost(noffH->readonlyData.inFileAddr); #endif - noffH->initData.size = WordToHost(noffH->initData.size); - noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr); - noffH->initData.inFileAddr = WordToHost(noffH->initData.inFileAddr); - noffH->uninitData.size = WordToHost(noffH->uninitData.size); - noffH->uninitData.virtualAddr = WordToHost(noffH->uninitData.virtualAddr); - noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr); + noffH->initData.size = WordToHost(noffH->initData.size); + noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr); + noffH->initData.inFileAddr = WordToHost(noffH->initData.inFileAddr); + noffH->uninitData.size = WordToHost(noffH->uninitData.size); + noffH->uninitData.virtualAddr = WordToHost(noffH->uninitData.virtualAddr); + noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr); #ifdef RDATA - DEBUG(dbgAddr, "code = " << noffH->code.size << - " readonly = " << noffH->readonlyData.size << - " init = " << noffH->initData.size << - " uninit = " << noffH->uninitData.size << "\n"); + DEBUG(dbgAddr, "code = " << noffH->code.size << + " readonly = " << noffH->readonlyData.size << + " init = " << noffH->initData.size << + " uninit = " << noffH->uninitData.size << "\n"); #endif } -FrameTable::Node::Node(int idx): - next(nullptr), idx(idx) {} - -FrameTable::FrameTable() -{ - available = NumPhysPages; - useCount = new int[NumPhysPages]; - begin = end = new FrameTable::Node; - for (int i = 0; i < NumPhysPages; i++) { - useCount[i] = 0; - end->idx = i; - end->next = new FrameTable::Node; - end = end->next; - } -} - -FrameTable::~FrameTable() -{ - delete[] useCount; - while (begin != end) { - FrameTable::Node *tmpNode = begin; - begin = begin->next; - delete tmpNode; - } - delete begin; -} - -int FrameTable::Allocate() -{ - if (available == 0) - return -1; - - int ret = begin->idx; - Node *tmp = begin; - begin = begin->next; - delete tmp; - - --available; - useCount[ret]++; - - bzero(kernel->machine->mainMemory + ret * PageSize, PageSize); - - //cerr << "Allocated at page: " << ret << endl; - return ret; -} - -void FrameTable::Release(int phyPageNum) -{ - useCount[phyPageNum]--; - - if (useCount[phyPageNum] > 0) - return; - - ++available; - - //cerr << "Release page: " << end->idx << endl; - end->idx = phyPageNum; - end->next = new FrameTable::Node; -} - -size_t FrameTable::RemainSize() -{ - return available; -} - //---------------------------------------------------------------------- // AddrSpace::AddrSpace // Create an address space to run a user program. @@ -129,9 +63,24 @@ size_t FrameTable::RemainSize() // 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 = 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); + */ +} //---------------------------------------------------------------------- // AddrSpace::~AddrSpace @@ -140,10 +89,9 @@ AddrSpace::AddrSpace() AddrSpace::~AddrSpace() { - for (int i = 0; i < NumPhysPages; i++) - if (pageTable[i].use == TRUE) - kernel->frameTable->Release(pageTable[i].physicalPage); - delete[] pageTable; + /* delete pageTable; */ + // release frame table by page table + kernel->frameTable->Release(pageTable, numPages); } @@ -156,114 +104,140 @@ AddrSpace::~AddrSpace() // // "fileName" is the file containing the object code to load into memory //---------------------------------------------------------------------- - -bool -AddrSpace::Load(char *fileName) +bool +AddrSpace::Load(char* fileName) { - //cerr << "AddrSpace::Load" << endl; - OpenFile *executable = kernel->fileSystem->Open(fileName); - NoffHeader noffH; - unsigned int size; + OpenFile* executable = kernel->fileSystem->Open(fileName); + NoffHeader noffH; + unsigned int size; - if (executable == NULL) { - cerr << "Unable to open file " << fileName << "\n"; - return FALSE; - } + if (executable == NULL) { + cerr << "Unable to open file " << fileName << "\n"; + return FALSE; + } - executable->ReadAt((char *)&noffH, sizeof(noffH), 0); - if ((noffH.noffMagic != NOFFMAGIC) && - (WordToHost(noffH.noffMagic) == NOFFMAGIC)) - SwapHeader(&noffH); - ASSERT(noffH.noffMagic == NOFFMAGIC); + executable->ReadAt((char*)&noffH, sizeof(noffH), 0); + if ((noffH.noffMagic != NOFFMAGIC) && + (WordToHost(noffH.noffMagic) == NOFFMAGIC)) + SwapHeader(&noffH); + ASSERT(noffH.noffMagic == NOFFMAGIC); #ifdef RDATA -// how big is address space? - size = noffH.code.size + noffH.readonlyData.size + noffH.initData.size + - noffH.uninitData.size + UserStackSize; - //cerr << noffH.code.size << ' ' - // << noffH.readonlyData.size << ' ' - // << noffH.initData.size << ' ' - // << noffH.uninitData.size << ' ' - // << UserStackSize << endl; - // we need to increase the size - // to leave room for the stack + // how big is address space? + size = noffH.code.size + noffH.readonlyData.size + noffH.initData.size + + noffH.uninitData.size + UserStackSize; + // we need to increase the size +// to leave room for the stack #else -// how big is address space? - size = noffH.code.size + noffH.initData.size + noffH.uninitData.size - + UserStackSize; // we need to increase the size - // to leave room for the stack - //cerr << noffH.code.size << ' ' - // << noffH.initData.size << ' ' - // << noffH.uninitData.size << ' ' - // << UserStackSize << endl; + // 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; + 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 = new TranslationEntry[numPages]; - for (int i = 0; i < numPages; i++) { - pageTable[i].virtualPage = i; - pageTable[i].physicalPage = -1; - pageTable[i].valid = TRUE; - pageTable[i].use = FALSE; - pageTable[i].dirty = FALSE; - pageTable[i].readOnly = FALSE; - } - - DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size); - -// then, copy in the code and data segments into memory - if (noffH.code.size > 0) { - DEBUG(dbgAddr, "Initializing code segment."); - DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size); - - for (size_t cur = 0; cur < (size_t)noffH.code.size; cur += PageSize) { - size_t physAddr, size = min((size_t)PageSize, noffH.code.size - cur); - Translate(noffH.code.virtualAddr + cur, &physAddr, 1); - //cerr << "physAddr, size: " << physAddr << ' ' << size << endl; - - executable->ReadAt( - &(kernel->machine->mainMemory[physAddr]), size, - noffH.code.inFileAddr + cur); + pageTable = kernel->frameTable->Allocate(numPages); + if (!pageTable) { + kernel->interrupt->setStatus(SystemMode); + ExceptionHandler(MemoryLimitException); + kernel->interrupt->setStatus(UserMode); } - } - if (noffH.initData.size > 0) { - DEBUG(dbgAddr, "Initializing data segment."); - DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size); - for (size_t cur = 0; cur < (size_t)noffH.initData.size; cur += PageSize) { - size_t physAddr, size = min((size_t)PageSize, noffH.initData.size - cur); - Translate(noffH.initData.virtualAddr + cur, &physAddr, 1); + DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size); - executable->ReadAt( - &(kernel->machine->mainMemory[physAddr]), size, - noffH.initData.inFileAddr + cur); + // 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); + + 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); + + 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); + if (noffH.readonlyData.size > 0) { + DEBUG(dbgAddr, "Initializing read only data segment."); + DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size); - for (size_t cur = 0; cur < (size_t)noffH.readonlyData.size; cur += PageSize) { - size_t physAddr, size = min((size_t)PageSize, noffH.readonlyData.size - cur); - Translate(noffH.readonlyData.virtualAddr + cur, &physAddr, 1); + sz = noffH.readonlyData.size; + vaddr = noffH.readonlyData.virtualAddr; + fpos = noffH.readonlyData.inFileAddr; - executable->ReadAt( - &(kernel->machine->mainMemory[physAddr]), - size, noffH.readonlyData.inFileAddr + cur); + // 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 - delete executable; // close file - return TRUE; // success + delete executable; // close file + return TRUE; // success } //---------------------------------------------------------------------- @@ -274,21 +248,20 @@ AddrSpace::Load(char *fileName) // the address space // //---------------------------------------------------------------------- - -void -AddrSpace::Execute(char* fileName) +void +AddrSpace::Execute(char* fileName) { - //cerr << "AddrSpace::Execute" << endl; - kernel->currentThread->space = this; - this->InitRegisters(); // set the initial register values - this->RestoreState(); // load page table register + kernel->currentThread->space = this; - kernel->machine->Run(); // jump to the user progam + this->InitRegisters(); // set the initial register values + this->RestoreState(); // load page table register - ASSERTNOTREACHED(); // machine->Run never returns; - // the address space exits - // by doing the syscall "exit" + kernel->machine->Run(); // jump to the user program + + ASSERTNOTREACHED(); // machine->Run never returns; + // the address space exits + // by doing the syscall "exit" } @@ -301,31 +274,30 @@ 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; - int i; + Machine* machine = kernel->machine; + int i; - for (i = 0; i < NumTotalRegs; i++) - machine->WriteRegister(i, 0); + for (i = 0; i < NumTotalRegs; i++) + machine->WriteRegister(i, 0); - // Initial program counter -- must be location of "Start", which - // is assumed to be virtual address zero - machine->WriteRegister(PCReg, 0); + // Initial program counter -- must be location of "Start", which + // is assumed to be virtual address zero + machine->WriteRegister(PCReg, 0); - // Need to also tell MIPS where next instruction is, because - // of branch delay possibility - // Since instructions occupy four bytes each, the next instruction - // after start will be at virtual address four. - machine->WriteRegister(NextPCReg, 4); + // Need to also tell MIPS where next instruction is, because + // of branch delay possibility + // Since instructions occupy four bytes each, the next instruction + // after start will be at virtual address four. + machine->WriteRegister(NextPCReg, 4); - // Set the stack register to the end of the address space, where we - // allocated the stack; but subtract off a bit, to make sure we don't - // accidentally reference off the end! - machine->WriteRegister(StackReg, numPages * PageSize - 16); - DEBUG(dbgAddr, "Initializing stack pointer: " << numPages * PageSize - 16); + // 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); } //---------------------------------------------------------------------- @@ -335,9 +307,9 @@ AddrSpace::InitRegisters() // // For now, don't need to save anything! //---------------------------------------------------------------------- - -void AddrSpace::SaveState() -{} +void AddrSpace::SaveState() +{ +} //---------------------------------------------------------------------- // AddrSpace::RestoreState @@ -346,11 +318,10 @@ 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; + kernel->machine->pageTable = pageTable; + kernel->machine->pageTableSize = numPages; } @@ -363,50 +334,84 @@ 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; - int pfn; - unsigned int vpn = vaddr / PageSize; - unsigned int offset = vaddr % PageSize; + TranslationEntry* pte; + int pfn; + unsigned int vpn = vaddr / PageSize; + unsigned int offset = vaddr % PageSize; - if(vpn >= numPages) { - return AddressErrorException; - } - - pte = &pageTable[vpn]; - - if(isReadWrite && pte->readOnly) { - return ReadOnlyException; - } - - pfn = pte->physicalPage; - if (pfn == -1) { - pfn = pte->physicalPage = kernel->frameTable->Allocate(); - if (pfn == -1) { - DEBUG(dbgAddr, "Memory Limit exceeded"); - return MemoryLimitException; + if (vpn >= numPages) { + return AddressErrorException; } - } - // if the pageFrame is too big, there is something really wrong! - // An invalid translation was loaded into the page table or TLB. - if (pfn >= NumPhysPages) { - DEBUG(dbgAddr, "Illegal physical page " << pfn); - return BusErrorException; - } + pte = &pageTable[vpn]; - pte->use = TRUE; // set the use, dirty bits + if (isReadWrite && pte->readOnly) { + return ReadOnlyException; + } - if(isReadWrite) - pte->dirty = TRUE; + pfn = pte->physicalPage; - *paddr = pfn * PageSize + offset; + // if the pageFrame is too big, there is something really wrong! + // An invalid translation was loaded into the page table or TLB. + if (pfn >= NumPhysPages) { + DEBUG(dbgAddr, "Illegal physical page " << pfn); + return BusErrorException; + } - ASSERT((*paddr < MemorySize)); + pte->use = TRUE; // set the use, dirty bits - //cerr << " -- AddrSpace::Translate(): vaddr: " << vaddr << - // ", paddr: " << *paddr << "\n"; + if (isReadWrite) + pte->dirty = TRUE; - return NoException; + *paddr = pfn * PageSize + offset; + + ASSERT((*paddr < MemorySize)); + + //cerr << " -- AddrSpace::Translate(): vaddr: " << vaddr << + // ", paddr: " << *paddr << "\n"; + + 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; +} + + diff --git a/code/userprog/addrspace.h b/code/userprog/addrspace.h index fa9c104..0da4575 100644 --- a/code/userprog/addrspace.h +++ b/code/userprog/addrspace.h @@ -18,57 +18,84 @@ #define UserStackSize 1024 // increase this as necessary! -class FrameTable { - public: - FrameTable(); - ~FrameTable(); - - int Allocate(); - void Release(int phyPageNum); - size_t RemainSize(); - - private: - struct Node { - Node *next; - int idx; - Node(int idx = -1); - }; - - Node *begin, *end; - - size_t available; - int *useCount; -}; - 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; - 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 available; +}; + +#endif /* FRAME_TABLE_H */ \ No newline at end of file diff --git a/code/userprog/exception.cc b/code/userprog/exception.cc index c7be4ba..a9363d1 100644 --- a/code/userprog/exception.cc +++ b/code/userprog/exception.cc @@ -1,18 +1,18 @@ // exception.cc -// Entry point into the Nachos kernel from user programs. -// There are two kinds of things that can cause control to -// transfer back to here from user code: +// Entry point into the Nachos kernel from user programs. +// There are two kinds of things that can cause control to +// transfer back to here from user code: // -// syscall -- The user code explicitly requests to call a procedure -// in the Nachos kernel. Right now, the only function we support is -// "Halt". +// syscall -- The user code explicitly requests to call a procedure +// in the Nachos kernel. Right now, the only function we support is +// "Halt". // -// exceptions -- The user code does something that the CPU can't handle. -// For instance, accessing memory that doesn't exist, arithmetic errors, -// etc. +// exceptions -- The user code does something that the CPU can't handle. +// For instance, accessing memory that doesn't exist, arithmetic errors, +// etc. // -// Interrupts (which can also cause control to transfer from user -// code into the Nachos kernel) are handled elsewhere. +// Interrupts (which can also cause control to transfer from user +// code into the Nachos kernel) are handled elsewhere. // // For now, this only handles the Halt() system call. // Everything else core dumps. @@ -27,192 +27,170 @@ #include "ksyscall.h" //---------------------------------------------------------------------- // ExceptionHandler -// Entry point into the Nachos kernel. Called when a user program -// is executing, and either does a syscall, or generates an addressing -// or arithmetic exception. +// Entry point into the Nachos kernel. Called when a user program +// is executing, and either does a syscall, or generates an addressing +// or arithmetic exception. // -// For system calls, the following is the calling convention: +// For system calls, the following is the calling convention: // -// system call code -- r2 -// arg1 -- r4 -// arg2 -- r5 -// arg3 -- r6 -// arg4 -- r7 +// system call code -- r2 +// arg1 -- r4 +// arg2 -- r5 +// arg3 -- r6 +// arg4 -- r7 // -// The result of the system call, if any, must be put back into r2. +// The result of the system call, if any, must be put back into r2. // // If you are handling a system call, don't forget to increment the pc // before returning. (Or else you'll loop making the same system call forever!) // -// "which" is the kind of exception. The list of possible exceptions -// is in machine.h. +// "which" is the kind of exception. The list of possible exceptions +// is in machine.h. //---------------------------------------------------------------------- void ExceptionHandler(ExceptionType which) { - int type = kernel->machine->ReadRegister(2); - int val; - int status, exit, threadID, programID; - DEBUG(dbgSys, "Received Exception " << which << " type: " << type << "\n"); - switch (which) { - case SyscallException: - switch(type) { - case SC_Halt: - DEBUG(dbgSys, "Shutdown, initiated by user program.\n"); - SysHalt(); - cout<<"in exception\n"; - ASSERTNOTREACHED(); - break; - case SC_MSG: - DEBUG(dbgSys, "Message received.\n"); - val = kernel->machine->ReadRegister(4); - { - char *msg = &(kernel->machine->mainMemory[val]); - cout << msg << endl; - } - SysHalt(); - ASSERTNOTREACHED(); - break; - case SC_Create: - val = kernel->machine->ReadRegister(4); - { - char *filename = &(kernel->machine->mainMemory[val]); - //cout << filename << endl; - status = SysCreate(filename); - kernel->machine->WriteRegister(2, (int) status); - } - kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg)); - kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4); - kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4); - return; - ASSERTNOTREACHED(); - break; - case SC_Add: - DEBUG(dbgSys, "Add " << kernel->machine->ReadRegister(4) << " + " << kernel->machine->ReadRegister(5) << "\n"); - /* Process SysAdd Systemcall*/ - int result; - result = SysAdd(/* int op1 */(int)kernel->machine->ReadRegister(4), - /* int op2 */(int)kernel->machine->ReadRegister(5)); - DEBUG(dbgSys, "Add returning with " << result << "\n"); - /* Prepare Result */ - kernel->machine->WriteRegister(2, (int)result); - /* Modify return point */ - { - /* set previous programm counter (debugging only)*/ - kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg)); - - /* set programm counter to next instruction (all Instructions are 4 byte wide)*/ - kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4); - - /* set next programm counter for brach execution */ - kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4); - } - cout << "result is " << result << "\n"; - return; - ASSERTNOTREACHED(); - break; - case SC_Exit: - DEBUG(dbgAddr, "Program exit\n"); - val = kernel->machine->ReadRegister(4); - cout << "return value:" << val << endl; - kernel->currentThread->Finish(); - break; - case SC_PrintInt: - DEBUG(dbgAddr, "Printing int\n"); - val = (int)kernel->machine->ReadRegister(4); - SysPrintInt(val); + int type = kernel->machine->ReadRegister(2); + int val; + 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: + DEBUG(dbgSys, "Shutdown, initiated by user program.\n"); + SysHalt(); + cout << "in exception\n"; + ASSERTNOTREACHED(); + break; + case SC_MSG: + DEBUG(dbgSys, "Message received.\n"); + val = kernel->machine->ReadRegister(4); + { + char* msg = &(kernel->machine->mainMemory[val]); + cout << msg << endl; + } + SysHalt(); + ASSERTNOTREACHED(); + break; + case SC_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); + } + 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_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)); + DEBUG(dbgSys, "Add returning with " << result << "\n"); + /* Prepare Result */ + kernel->machine->WriteRegister(2, (int)result); + /* Modify return point */ + { + /* set previous programm counter (debugging only)*/ + kernel->machine->WriteRegister(PrevPCReg, kernel->machine->ReadRegister(PCReg)); - 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_Open: - DEBUG(dbgAddr, "Open file\n"); + /* set programm counter to next instruction (all Instructions are 4 byte wide)*/ + kernel->machine->WriteRegister(PCReg, kernel->machine->ReadRegister(PCReg) + 4); - { - val = kernel->machine->ReadRegister(4); - char *name = &(kernel->machine->mainMemory[val]); - OpenFileId ret = SysOpen(name); - kernel->machine->WriteRegister(2, ret); - } - - 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: - DEBUG(dbgAddr, "Read file\n"); - - { - val = kernel->machine->ReadRegister(4); - char *buffer = &(kernel->machine->mainMemory[val]); - int size = kernel->machine->ReadRegister(5); - OpenFileId id = (OpenFileId)kernel->machine->ReadRegister(6); - int ret = SysRead(buffer, size, id); - kernel->machine->WriteRegister(2, ret); - } - - 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: - DEBUG(dbgAddr, "Write file\n"); - - { - val = kernel->machine->ReadRegister(4); - char *buffer = &(kernel->machine->mainMemory[val]); - int size = kernel->machine->ReadRegister(5); - OpenFileId id = (OpenFileId)kernel->machine->ReadRegister(6); - // fprintf(stderr, "buffer: %p\n", buffer); - // cerr << "size: " << size << endl; - // cerr << "id: " << id << endl; - int ret = SysWrite(buffer, size, id); - kernel->machine->WriteRegister(2, ret); - } - - 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: - DEBUG(dbgAddr, "Close file\n"); - - { - OpenFileId id = (OpenFileId)kernel->machine->ReadRegister(4); - int ret = SysClose(id); - kernel->machine->WriteRegister(2, ret); - } - - 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; - default: - cerr << "Unexpected system call " << type << "\n"; - break; - } - break; - default: - cerr << "Unexpected user mode exception " << (int)which << "\n"; - break; - } - ASSERTNOTREACHED(); + /* set next programm counter for brach execution */ + kernel->machine->WriteRegister(NextPCReg, kernel->machine->ReadRegister(PCReg) + 4); + } + cout << "result is " << result << "\n"; + return; + ASSERTNOTREACHED(); + break; + case SC_Exit: + DEBUG(dbgAddr, "Program exit\n"); + val = kernel->machine->ReadRegister(4); + cout << "return value:" << val << endl; + kernel->currentThread->Finish(); + break; + default: + cerr << "Unexpected system call " << type << "\n"; + break; + } + break; + default: + cerr << "Unexpected user mode exception " << (int)which << "\n"; + break; + } + ASSERTNOTREACHED(); } diff --git a/code/userprog/hw4_consoleIO_1.c b/code/userprog/hw4_consoleIO_1.c new file mode 100644 index 0000000..ef6c61b --- /dev/null +++ b/code/userprog/hw4_consoleIO_1.c @@ -0,0 +1,11 @@ +#include "syscall.h" + +int +main() +{ + int n; + for (n = 0; n < 4; n++) { + PrintInt(1); + } + return 0; +} diff --git a/code/userprog/hw4_consoleIO_2.c b/code/userprog/hw4_consoleIO_2.c new file mode 100644 index 0000000..685d7d4 --- /dev/null +++ b/code/userprog/hw4_consoleIO_2.c @@ -0,0 +1,11 @@ +#include "syscall.h" + +int +main() +{ + int n; + for (n = 0; n < 5; n++) { + PrintInt(2); + } + return 0; +} diff --git a/code/userprog/hw4_consoleIO_3.c b/code/userprog/hw4_consoleIO_3.c new file mode 100644 index 0000000..9092129 --- /dev/null +++ b/code/userprog/hw4_consoleIO_3.c @@ -0,0 +1,12 @@ +#include "syscall.h" + +int +main() +{ + int n; + + for (n = 0; n < 12; n++) { + PrintInt(3); + } + return 0; +} diff --git a/code/userprog/hw4_consoleIO_4.c b/code/userprog/hw4_consoleIO_4.c new file mode 100644 index 0000000..70877e7 --- /dev/null +++ b/code/userprog/hw4_consoleIO_4.c @@ -0,0 +1,12 @@ +#include "syscall.h" + +int +main() +{ + int n; + + for (n = 0; n < 11; n++) { + PrintInt(4); + } + return 0; +} diff --git a/code/userprog/ksyscall.h b/code/userprog/ksyscall.h index 6b315f7..309f873 100644 --- a/code/userprog/ksyscall.h +++ b/code/userprog/ksyscall.h @@ -1,78 +1,71 @@ -/************************************************************** - * - * userprog/ksyscall.h - * - * Kernel interface for systemcalls - * - * by Marcus Voelp (c) Universitaet Karlsruhe - * - **************************************************************/ - -#ifndef __USERPROG_KSYSCALL_H__ -#define __USERPROG_KSYSCALL_H__ - -#define INT_BUF_LENGTH 13 - -#include "kernel.h" - -#include "synchconsole.h" - - -void SysHalt() -{ - kernel->interrupt->Halt(); -} - -int SysAdd(int op1, int op2) -{ - return op1 + op2; -} - -int SysCreate(char *filename) -{ - // return value - // 1: success - // 0: failed - return kernel->interrupt->CreateFile(filename); -} - -void SysPrintInt(int value) { - kernel->interrupt->PrintInt(value); -} - -OpenFileId SysOpen(char *name) { - OpenFileId id = -1; - for (int i = 0; i < 20; i++) - if (kernel->fileSystem->fileDescriptorTable[i] == NULL) { - id = i; - kernel->fileSystem->fileDescriptorTable[i] - = kernel->fileSystem->Open(name); - if (kernel->fileSystem->fileDescriptorTable[i] == NULL) - return -1; - break; - } - return id; -} - -int SysWrite(char *buffer, int size, OpenFileId id) { - if (id < 0 || id >= 20 || kernel->fileSystem->fileDescriptorTable[id] == NULL) - return -1; - return kernel->fileSystem->fileDescriptorTable[id]->Write(buffer, size); -} - -int SysRead(char *buffer, int size, OpenFileId id) { - if (id < 0 || id >= 20 || kernel->fileSystem->fileDescriptorTable[id] == NULL) - return -1; - return kernel->fileSystem->fileDescriptorTable[id]->Read(buffer, size); -} - -int SysClose(OpenFileId id) { - if (id < 0 || id >= 20 || kernel->fileSystem->fileDescriptorTable[id] == NULL) - return 0; - delete kernel->fileSystem->fileDescriptorTable[id]; - kernel->fileSystem->fileDescriptorTable[id] = NULL; - return 1; -} - - -#endif /* ! __USERPROG_KSYSCALL_H__ */ +/************************************************************** + * + * userprog/ksyscall.h + * + * Kernel interface for systemcalls + * + * by Marcus Voelp (c) Universitaet Karlsruhe + * + **************************************************************/ + +#ifndef __USERPROG_KSYSCALL_H__ +#define __USERPROG_KSYSCALL_H__ + +#include "kernel.h" + +#include "synchconsole.h" + +typedef int OpenFileId; + +void SysHalt() +{ + kernel->interrupt->Halt(); +} + +int SysAdd(int op1, int op2) +{ + return op1 + op2; +} + +int SysCreate(char* filename) +{ + // return value + // 1: success + // 0: failed + return kernel->interrupt->CreateFile(filename); +} + +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__ */ diff --git a/code/userprog/noff.h b/code/userprog/noff.h index a8b5551..7fda56f 100644 --- a/code/userprog/noff.h +++ b/code/userprog/noff.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; diff --git a/code/userprog/synchconsole.cc b/code/userprog/synchconsole.cc index da76e3c..4f03dfc 100644 --- a/code/userprog/synchconsole.cc +++ b/code/userprog/synchconsole.cc @@ -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,13 +106,18 @@ 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(); + lock->Acquire(); + consoleOutput->PutInt(value); + waitFor->P(); + lock->Release(); } //---------------------------------------------------------------------- diff --git a/code/userprog/synchconsole.h b/code/userprog/synchconsole.h index 9c2ff47..9dd7865 100644 --- a/code/userprog/synchconsole.h +++ b/code/userprog/synchconsole.h @@ -21,34 +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 - void PutInt(int value); - - 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 diff --git a/code/userprog/syscall.h b/code/userprog/syscall.h index ce6dd48..4f76da2 100644 --- a/code/userprog/syscall.h +++ b/code/userprog/syscall.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,83 +15,86 @@ #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 #define SC_Join 3 #define SC_Create 4 -#define SC_Remove 5 +#define SC_Remove 5 #define SC_Open 6 #define SC_Read 7 #define SC_Write 8 -#define SC_Seek 9 -#define SC_Close 10 -#define SC_ThreadFork 11 +#define SC_Seek 9 +#define SC_Close 10 +#define SC_ThreadFork 11 #define SC_ThreadYield 12 -#define SC_ExecV 13 +#define SC_ExecV 13 #define SC_ThreadExit 14 #define SC_ThreadJoin 15 - -#define SC_PrintInt 16 - -#define SC_Add 42 -#define SC_MSG 100 +#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 @@ -101,45 +104,45 @@ 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. */ #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); +#define SysConsoleOutput 1 + + /* Create a Nachos file, with name "name" */ + /* Note: Create does not open the file. */ + /* Return 1 on success, negative error code on failure */ +int Create(char* name); /* Remove a Nachos file, with name "name" */ -int Remove(char *name); +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". @@ -153,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. @@ -180,8 +183,6 @@ int ThreadJoin(ThreadId id); */ void ThreadExit(int ExitCode); -void PrintInt(int number); - #endif /* IN_ASM */ #endif /* SYSCALL_H */ diff --git a/docker-compose.yaml b/docker-compose.yaml index 06cced5..d4adafa 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,6 +2,6 @@ services: test: build: . - user: '60139:60139' + user: ytshih volumes: - './:/work'