📄 exception.cpp
字号:
// exception.cc // Entry point into the Nachos kernel from user programs.// There are two kinds of things that can cause control to// transfer back to here from user code://// syscall -- The user code explicitly requests to call a procedure// in the Nachos kernel. Right now, the only function we support is// "Halt".//// exceptions -- The user code does something that the CPU can't handle.// For instance, accessing memory that doesn't exist, arithmetic errors,// etc. //// Interrupts (which can also cause control to transfer from user// code into the Nachos kernel) are handled elsewhere.//// For now, this only handles the Halt() system call.// Everything else core dumps.//// Copyright (c) 1992-1993 The Regents of the University of California.// All rights reserved. See copyright.h for copyright notice and limitation // of liability and disclaimer of warranty provisions.#include "copyright.h"#include "system.h"#include "syscall.h"#include "machine.h"#include "addrspace.h"#include "exception.h"extern void MPTest(int);extern void StartProcess(char *file);static void updatePCReg(void);static void initConsole(void);static void loadExecutable (Thread *, int addr);static void startThread();//----------------------------------------------------------------------// ExceptionHandler// Entry point into the Nachos kernel. Called when a user program// is executing, and either does a syscall, or generates an addressing// or arithmetic exception.////// For system calls, the following is the calling convention://// system call code -- r2// arg1 -- r4// arg2 -- r5// arg3 -- r6// arg4 -- r7//// The result of the system call, if any, must be put back into r2. //// And 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 // are in machine.h.//----------------------------------------------------------------------voidExceptionHandler(ExceptionType which){ int type = machine->ReadRegister(2); initConsole();//----------------------------------------------------------------------- // Exception Handler handles system calls from a user program// The following system calls have been implemented:// 1.Halt() halt the "NACHOS" system!!// 2.Exit() exit from the current user process, i.e kill it// 3.Exec() create a new process and execute it// 4.Write() for writing text into a file// 5.Read() for reading from a file// 6.Join() for resuming after an Exec() call//----------------------------------------------------------------------- if ((which == SyscallException) && (type == SC_Halt)) { DEBUG('a', "Shutdown, initiated by user program.\n"); delete console; delete writeLock; delete readAvail; delete writeDone; interrupt->Halt(); } else if((which == SyscallException) && (type == SC_Exit)) { int exitStatus = machine->ReadRegister(4); DEBUG('t', "Program exiting on status %d...\n", exitStatus); machine->WriteRegister(2,exitStatus); updatePCReg(); DEBUG('t',"Current Joinvalue %d\n",joinInvoked); threadsIn--; int findThread = 0; if ( joinInvoked != 0) { while (currentThread != joinThreadList.threadExecuting[findThread]) findThread++; joinThreadList.threadExecuting[findThread] = (Thread *)NULL; DEBUG('t',"Thread sending Broadcast is %d\n",findThread); joinThreadList.completeThread[findThread]->Broadcast (joinThreadList.completeLock[findThread]); statusReturned[findThread] = exitStatus; DEBUG('t', "Status Returned When Waking %d\n", statusReturned[findThread]); } DEBUG('t',"Current thread completing %d\n",findThread); threadComplete[findThread] = 1; DEBUG('t',"Current Joinvalue %d\n",joinInvoked); // releasing memory //delete currentThread->space; currentThread->Finish(); } else if ((which == SyscallException) && (type == SC_Exec)) { int addr = (int)machine->ReadRegister(4); updatePCReg(); threadsIn++; Thread * execThread = new Thread("execThread"); joinThreadList.threadExecuting[numThreadsExec] = execThread; joinThreadList.completeThread[numThreadsExec] = new Condition("New Thread Executing"); threadComplete[numThreadsExec] = 0; DEBUG('t',"Executing a child thread...\n"); DEBUG('t',"The thread id is %d\n", numThreadsExec); machine->WriteRegister(2, numThreadsExec); numThreadsExec ++; loadExecutable(execThread, addr); execThread->Fork((VoidFunctionPtr)startThread,addr); } else if ((which == SyscallException) && (type == SC_Join)) { int addr = (int)machine->ReadRegister(4); DEBUG('a',"The joining wait is on %d\n",addr); updatePCReg(); joinThreadList.completeLock[addr] = new Lock("Dummy Lock"); joinThreadList.completeLock[addr]->Acquire(); IntStatus oldLevel = interrupt->SetLevel(IntOff); if ((threadToBeDestroyed != joinThreadList.threadExecuting[addr]) || (threadComplete[addr] == 0)) { joinInvoked++; DEBUG('a',"New Joinvalue after ++ %d\n",joinInvoked); joinThreadList.completeThread[addr]->Wait (joinThreadList.completeLock[addr]); joinInvoked--; DEBUG('a',"New Joinvalue after -- %d\n",joinInvoked); } (void) interrupt->SetLevel(oldLevel); joinThreadList.completeLock[addr]->Release(); machine->WriteRegister(2,statusReturned[addr]); DEBUG('a', "Status Returned after Waking %d\n",statusReturned[addr]); DEBUG('a',"The thread woken up is %d\n",addr); } else if((which == SyscallException) && (type == SC_Write)) { int addr = (int)machine->ReadRegister(4); int Size = (int)machine->ReadRegister(5); OpenFileId writeHandle = (int)machine->ReadRegister(6); ASSERT(writeHandle == ConsoleOutput); DEBUG('a', "Address of buffer %d\n", addr); DEBUG('a', "Size of buffer %d\n",Size ); char buffer[Size+1]; int oneChar, i=0; // Transfer buffer content from user space to kernel space machine->ReadMem(addr, 1, &oneChar); while((i <Size) && (oneChar != '\0')) { buffer[i] = oneChar; machine->ReadMem(++addr, 1, &oneChar); i++; } buffer[i] = 0; Size = i; i = 0; while (buffer[i] != 0 && i < Size) { IntStatus oldLevel = interrupt->SetLevel(IntOff); writeLock->Acquire(); // wait for write process to be done console->PutChar(buffer[i++]); writeDone->P(); (void) interrupt->SetLevel(oldLevel); writeLock->Release(); } updatePCReg(); } else if((which == SyscallException) && (type == SC_TestCase)) { updatePCReg(); // run with interrupts off, just in case: IntStatus oldLevel = interrupt->SetLevel(IntOff); TestCase(machine->ReadRegister(4)); (void) interrupt->SetLevel(oldLevel); } else if (which == PageFaultException) { stats->numPageFaults++; int BadVPage=(machine->ReadRegister(BadVAddrReg)/PageSize); machine->memManager->PageFaultExceptionHandler(BadVPage); } else if (which == IllegalInstrException) { printf("Illegal Instruction: EXITING...\n"); currentThread->Finish(); } #ifdef CHANGE else { printf("Unkown Exception.\n"); interrupt->Halt(); } #endif} //-----------------------------------------------------------------// Updating the Program Counter before returning to the User mode// ensures that the "SYSCALL" instruction is not executed forever.// Don't use this for page faults, because you want to retry the// faulting instruction, not skip it!//-----------------------------------------------------------------void updatePCReg(void){ int pc = machine->ReadRegister(PCReg); machine->WriteRegister(PrevPCReg,pc); pc = machine->ReadRegister(NextPCReg); machine->WriteRegister(PCReg,pc); pc += 4; machine->WriteRegister(NextPCReg,pc);}// This initialization should only be done once.void initConsole(void){ static int Initialized = 0; if (Initialized == 0) { Initialized = 1; readAvail = new Semaphore("readAvail", 0); writeDone = new Semaphore("writeDone", 0); writeLock = new Lock("writeLock"); console = new Console((char *) NULL, (char *) NULL, ReadAvail, WriteDone, 0); }}// This code is for the Exec call, to do error checking and load the// executable file into memory.void loadExecutable(Thread *execThread, int addr){ char filename[20]; int fileptr, i = 0; for (int ckm = 0; ckm < 20; ckm ++) filename[ckm]='\0'; DEBUG('t',"Physical page now free %d\n", machine->memManager->memAvail()); DEBUG('t',"The address of filename is %d\n",addr); machine->ReadMem(addr, 1, &fileptr); while((char)fileptr != '\0') { filename[i] = (char)fileptr; addr++; machine->ReadMem(addr, 1,&fileptr); i++; } filename[i] = '\0'; DEBUG('t',"Filename to execute.. %s\n",filename); OpenFile *executable = fileSystem->Open(filename); if (executable == NULL) { printf("Unable to open file %s\n", filename); return; } AddrSpace *space = new AddrSpace(executable); execThread->space = space;}// Use Thread::Fork to call this; it starts the child running the program// for the exec call.void startThread(){ currentThread->space->InitRegisters(); // set the initial register values currentThread->space->RestoreState(); // load page table register machine->Run(); // jump to the user progam ASSERT(FALSE); // machine->Run never returns; // the address space exits // by doing the syscall "exit"}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -