# a. Trace code
## main.cc
entry point
```cpp=
kernel = new Kernel(argc, argv);
kernel->Initialize();
```
```cpp=
// finally, run an initial user program if requested to do so
kernel->ExecAll();
```
## Kernel::ExecAll()
* **execfileNum** represent the file need to be executed.
* call **Exec** function to execute every file in execfile table.
* call **Finish()** to terminate current thread.
```cpp=
void Kernel::ExecAll()
{
for (int i=1;i<=execfileNum;i++) {
int a = Exec(execfile[i]);
}
currentThread->Finish();
//Kernel::Exec();
}
```
## Kernel::Exec()
* return the id of executing thread.
* create new thread.
* Because creating a new thread,the thread number increment by 1.
* create address space for new thread.
* call **Thread::Fork()**.
```cpp=
int Kernel::Exec(char* name)
{
t[threadNum] = new Thread(name, threadNum);
t[threadNum]->space = new AddrSpace();
t[threadNum]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[threadNum]);
threadNum++;
return threadNum-1;
/*
cout << "Total threads number is " << execfileNum << endl;
for (int n=1;n<=execfileNum;n++) {
t[n] = new Thread(execfile[n]);
t[n]->space = new AddrSpace();
t[n]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[n]);
cout << "Thread " << execfile[n] << " is executing." << endl;
}
cout << "debug Kernel::Run finished.\n";
*/
// Thread *t1 = new Thread(execfile[1]);
// Thread *t1 = new Thread("../test/test1");
// Thread *t2 = new Thread("../test/test2");
// 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);
// currentThread->Finish();
// Kernel::Run();
// cout << "after ThreadedKernel:Run();" << endl; // unreachable
}
```
## Thread::Fork(VoidFunctionPtr func, void *arg)
* **func** store the function pointer.
* call **StackAllocate(func, arg)** to allocate stack size.
* call **interrupt->SetLevel()** to disable interrupt before calling scheduler.
* Calling ReadyToRun to fork.
* Enable interrupt.
```cpp=
void
Thread::Fork(VoidFunctionPtr func, void *arg)
{
Interrupt *interrupt = kernel->interrupt;
Scheduler *scheduler = kernel->scheduler;
IntStatus oldLevel;
DEBUG(dbgThread, "Forking thread: " << name << " f(a): " << (int) func << " " << arg);
StackAllocate(func, arg);
oldLevel = interrupt->SetLevel(IntOff);
scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts
// are disabled!
(void) interrupt->SetLevel(oldLevel);
}
```
## Thread::StackAllocate()
* call AllocBoundedArray system call to assign stack
* initalizing all registers in thread depending on the instruction set.
```cpp=
void
Thread::StackAllocate (VoidFunctionPtr func, void *arg)
{
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;
#endif
#ifdef SPARC
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 DECMIPS
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;
#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;
#endif
#ifdef PARISC
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;
#endif
}
```
## Kernel::ForkExecute()
* call **AddrSpace::Load()** to detect whether the thread is executable.
* call **AddrSpace::Execute()**
```cpp=
void ForkExecute(Thread *t)
{
if ( !t->space->Load(t->getName()) ) {
return; // executable not found
}
t->space->Execute(t->getName());
}
```
## AddrSpace::Execute()
* assgin current address space to kernel->currentThread
* call **InitRegisters()** to initalize the register.
* call **RestoreState()**.
* call **kernel->machine->Run()**.
```cpp=
void
AddrSpace::Execute(char* fileName)
{
kernel->currentThread->space = this;
this->InitRegisters(); // set the initial register values
this->RestoreState(); // load page table register
kernel->machine->Run(); // jump to the user progam
ASSERTNOTREACHED(); // machine->Run never returns;
// the address space exits
// by doing the syscall "exit"
}
```
## AddrSpace::InitRegisters()
* assgin 0 to every register.
* assgin 0 to program counter,we start with 0.
* assgin 4 to **NextPCReg** to record next instruction.
* assign the upperbound to **StackReg**.
```cpp=
void
AddrSpace::InitRegisters()
{
Machine *machine = kernel->machine;
int i;
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);
// 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);
}
```
## AddrSpace::RestoreState()
*
```cpp=
void AddrSpace::RestoreState()
{
kernel->machine->pageTable = pageTable;
kernel->machine->pageTableSize = numPages;
}
```
## Kernel::Kernel()
* argc: number of strings pointed by argv.
* argv: strings to store every argument.
* using **strcmp** in c standard libaray to handle argument type,if the argument need second parameter,it will use **ASSERT** to check.
```cpp=
Kernel::Kernel(int argc, char **argv)
{
randomSlice = FALSE;
debugUserProg = FALSE;
consoleIn = NULL; // default is stdin
consoleOut = NULL; // default is stdout
#ifndef FILESYS_STUB
formatFlag = FALSE;
#endif
reliability = 1; // network reliability, default is 1.0
hostName = 0; // machine id, also UNIX socket name
// 0 is the default machine id
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-rs") == 0) {
ASSERT(i + 1 < argc);
RandomInit(atoi(argv[i + 1]));// initialize pseudo-random
// number generator
randomSlice = TRUE;
i++;
} else if (strcmp(argv[i], "-s") == 0) {
debugUserProg = TRUE;
} else if (strcmp(argv[i], "-e") == 0) {
execfile[++execfileNum]= argv[++i];
cout << execfile[execfileNum] << "\n";
} else if (strcmp(argv[i], "-ci") == 0) {
ASSERT(i + 1 < argc);
consoleIn = argv[i + 1];
i++;
} else if (strcmp(argv[i], "-co") == 0) {
ASSERT(i + 1 < argc);
consoleOut = argv[i + 1];
i++;
#ifndef FILESYS_STUB
} else if (strcmp(argv[i], "-f") == 0) {
formatFlag = TRUE;
#endif
} else if (strcmp(argv[i], "-n") == 0) {
ASSERT(i + 1 < argc); // next argument is float
reliability = atof(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "-m") == 0) {
ASSERT(i + 1 < argc); // next argument is int
hostName = atoi(argv[i + 1]);
i++;
} else if (strcmp(argv[i], "-u") == 0) {
cout << "Partial usage: nachos [-rs randomSeed]\n";
cout << "Partial usage: nachos [-s]\n";
cout << "Partial usage: nachos [-ci consoleIn] [-co consoleOut]\n";
#ifndef FILESYS_STUB
cout << "Partial usage: nachos [-nf]\n";
#endif
cout << "Partial usage: nachos [-n #] [-m #]\n";
}
}
}
```
## Scheduler::ReadyToRun()
* Using **ASSERT** to check whether kernel disable interrrupt.
* set thread status to ready.
* append this thread to readyList.
```cpp=
void
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);
}
```
## CheckToBeDestroyed()
* If **toBeDestroyed** contains threads,we will delete it and set **toBeDestroyed** to NULL.
```cpp=
void
Scheduler::CheckToBeDestroyed()
{
if (toBeDestroyed != NULL) {
delete toBeDestroyed;
toBeDestroyed = NULL;
}
}
```
## AddrSpace::AddrSpace()
* **NumPhysPages**:number of physical pages available in the machime.
* create a pagetable and initalize data.
*
* calling bzero to clean the memory space.
```cpp=
AddrSpace::AddrSpace()
{
pageTable = new TranslationEntry[NumPhysPages];
for (int i = 0; i < NumPhysPages; i++) {
pageTable[i].virtualPage = i; // for now, virt page # = phys page #
pageTable[i].physicalPage = i;
pageTable[i].valid = TRUE;
pageTable[i].use = FALSE;
pageTable[i].dirty = FALSE;
pageTable[i].readOnly = FALSE;
}
// zero out the entire address space
bzero(kernel->machine->mainMemory, MemorySize);
}
```
## Thread::Finish ()
* disable interrupt.
* call **Thread::Sleep()** to current thread.
```cpp=
void
Thread::Finish ()
{
(void) kernel->interrupt->SetLevel(IntOff);
ASSERT(this == kernel->currentThread);
DEBUG(dbgThread, "Finishing thread: " << name);
Sleep(TRUE); // invokes SWITCH
// not reached
}
```
## Thread::Sleep ()
* When entering this function ,interrupt should be disable,we use **ASSERT** to check that.
* set thread status to BLOCKED
* If the scheduler find out the ready queue is empty,it will let machine idle.
* call **Scheduler::Run()**.
```cpp=
void
Thread::Sleep (bool finishing)
{
Thread *nextThread;
ASSERT(this == kernel->currentThread);
ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Sleeping thread: " << name);
DEBUG(dbgTraCode, "In Thread::Sleep, Sleeping thread: " << name << ", " << kernel->stats->totalTicks);
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);
}
```
## Scheduler::Run ()
* **oldThread**: represents current runing thread.
* if oldThread space does not equal to null,call **SaveUserState()** and **SaveState()** to save current memory content.
* check whether **oldThread** is overflow.
* updating current running thread to **nextThread**,**nextThread** is the upcoming thread to execute.
* call SWITCH ...
* set the nextThread to status of running.
* call **CheckToBeDestroyed()** to delete oldThread.
```cpp=
void
Scheduler::Run ()
{
Thread *oldThread = kernel->currentThread;
ASSERT(kernel->interrupt->getLevel() == IntOff);
if (finishing) { // mark that we need to delete current thread
ASSERT(toBeDestroyed == NULL);
toBeDestroyed = oldThread;
}
if (oldThread->space != NULL) { // if this thread is a user program,
oldThread->SaveUserState(); // save the user's CPU registers
oldThread->space->SaveState();
}
oldThread->CheckOverflow(); // check if the old thread
// had an undetected stack overflow
kernel->currentThread = nextThread; // switch to the next thread
nextThread->setStatus(RUNNING); // nextThread is now running
DEBUG(dbgThread, "Switching from: " << oldThread->getName() << " to: " << nextThread->getName());
// This is a machine-dependent assembly language routine defined
// in switch.s. You may have to think
// a bit to figure out what happens after this, both from the point
// of view of the thread and from the perspective of the "outside world".
SWITCH(oldThread, nextThread);
// we're back, running oldThread
// interrupts are off when we return from switch!
ASSERT(kernel->interrupt->getLevel() == IntOff);
DEBUG(dbgThread, "Now in thread: " << oldThread->getName());
CheckToBeDestroyed(); // check if thread we were running
// before this one has finished
// and needs to be cleaned up
if (oldThread->space != NULL) { // if there is an address space
oldThread->RestoreUserState(); // to restore, do it.
oldThread->space->RestoreState();
}
}
```
## AddrSpace::Load()
* calculate the size of address space.
* calling divRoundUp(size, PageSize) to get the number of pages.
* using **numPages** to store the result
* calling kernel->fileSystem->Open(fileName) to open file.
* If we can oper file successfully,return true.Otherwise, return false.
```cpp=
bool
AddrSpace::Load(char *fileName)
{
OpenFile *executable = kernel->fileSystem->Open(fileName);
NoffHeader noffH;
unsigned int size;
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);
#ifdef RDATA
// 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
#endif
numPages = divRoundUp(size, PageSize);
size = numPages * PageSize;
ASSERT(numPages <= NumPhysPages); // check we're not trying
// to run anything too big --
// at least until we have
// virtual memory
DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size);
// then, copy in the code and data segments into memory
// Note: this code assumes that virtual address = physical address
if (noffH.code.size > 0) {
DEBUG(dbgAddr, "Initializing code segment.");
DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.code.virtualAddr]),
noffH.code.size, noffH.code.inFileAddr);
}
if (noffH.initData.size > 0) {
DEBUG(dbgAddr, "Initializing data segment.");
DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.initData.virtualAddr]),
noffH.initData.size, noffH.initData.inFileAddr);
}
#ifdef RDATA
if (noffH.readonlyData.size > 0) {
DEBUG(dbgAddr, "Initializing read only data segment.");
DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
executable->ReadAt(
&(kernel->machine->mainMemory[noffH.readonlyData.virtualAddr]),
noffH.readonlyData.size, noffH.readonlyData.inFileAddr);
}
#endif
delete executable; // close file
return TRUE; // success
}
```
## 跟address space 有關的檔案
* RDATA represens read only data.
* segment defined in noff.h
### segment definition
```cpp=
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 */
} Segment;
```
### noff header
```cpp=
typedef struct noffHeader {
int noffMagic; /* should be NOFFMAGIC */
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
*/
} NoffHeader;
```
## SwapHeader(NoffHeader *noffH)
* transform header bytes from little endian to big endian.
```cpp=
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);
#ifdef RDATA
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);
#ifdef RDATA
DEBUG(dbgAddr, "code = " << noffH->code.size << " readonly = " << noffH->readonlyData.size << " init = " << noffH->initData.size << " uninit = " << noffH->uninitData.size << "\n");
#endif
}
```
## OpenFile::ReadAt(char *into, int numBytes, int position)
```cpp=
int
OpenFile::ReadAt(char *into, int numBytes, int position)
{
int fileLength = hdr->FileLength();
int i, firstSector, lastSector, numSectors;
char *buf;
if ((numBytes <= 0) || (position >= fileLength))
return 0; // check request
if ((position + numBytes) > fileLength)
numBytes = fileLength - position;
DEBUG(dbgFile, "Reading " << numBytes << " bytes at " << position << " from file of length " << fileLength);
firstSector = divRoundDown(position, SectorSize);
lastSector = divRoundDown(position + numBytes - 1, SectorSize);
numSectors = 1 + lastSector - firstSector;
// read in all the full and partial sectors that we need
buf = new char[numSectors * SectorSize];
for (i = firstSector; i <= lastSector; i++)
kernel->synchDisk->ReadSector(hdr->ByteToSector(i * SectorSize),
&buf[(i - firstSector) * SectorSize]);
// copy the part we want
bcopy(&buf[position - (firstSector * SectorSize)], into, numBytes);
delete [] buf;
return numBytes;
}
```
# b. Implementattion
我有修改的部分
```cpp=
// kernel.cc
// Initialization and cleanup routines for the Nachos kernel.
//
// Copyright (c) 1992-1996 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 "debug.h"
#include "main.h"
#include "kernel.h"
#include "sysdep.h"
#include "synch.h"
#include "synchlist.h"
#include "libtest.h"
#include "string.h"
#include "synchdisk.h"
#include "post.h"
#include "synchconsole.h"
//----------------------------------------------------------------------
// Kernel::Kernel
// Interpret command line arguments in order to determine flags
// for the initialization (see also comments in main.cc)
//----------------------------------------------------------------------
Kernel::Kernel(int argc, char **argv)
{
randomSlice = FALSE;
debugUserProg = FALSE;
consoleIn = NULL; // default is stdin
consoleOut = NULL; // default is stdout
#ifndef FILESYS_STUB
formatFlag = FALSE;
#endif
reliability = 1; // network reliability, default is 1.0
hostName = 0; // machine id, also UNIX socket name
// 0 is the default machine id
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-rs") == 0)
{
ASSERT(i + 1 < argc);
RandomInit(atoi(argv[i + 1])); // initialize pseudo-random
// number generator
randomSlice = TRUE;
i++;
}
else if (strcmp(argv[i], "-s") == 0)
{
debugUserProg = TRUE;
}
else if (strcmp(argv[i], "-e") == 0)
{
execfile[++execfileNum] = argv[++i];
cout << execfile[execfileNum] << "\n";
}
else if (strcmp(argv[i], "-ci") == 0)
{
ASSERT(i + 1 < argc);
consoleIn = argv[i + 1];
i++;
}
else if (strcmp(argv[i], "-co") == 0)
{
ASSERT(i + 1 < argc);
consoleOut = argv[i + 1];
i++;
#ifndef FILESYS_STUB
}
else if (strcmp(argv[i], "-f") == 0)
{
formatFlag = TRUE;
#endif
}
else if (strcmp(argv[i], "-n") == 0)
{
ASSERT(i + 1 < argc); // next argument is float
reliability = atof(argv[i + 1]);
i++;
}
else if (strcmp(argv[i], "-m") == 0)
{
ASSERT(i + 1 < argc); // next argument is int
hostName = atoi(argv[i + 1]);
i++;
}
else if (strcmp(argv[i], "-u") == 0)
{
cout << "Partial usage: nachos [-rs randomSeed]\n";
cout << "Partial usage: nachos [-s]\n";
cout << "Partial usage: nachos [-ci consoleIn] [-co consoleOut]\n";
#ifndef FILESYS_STUB
cout << "Partial usage: nachos [-nf]\n";
#endif
cout << "Partial usage: nachos [-n #] [-m #]\n";
}
}
}
//----------------------------------------------------------------------
// Kernel::Initialize
// Initialize Nachos global data structures. Separate from the
// constructor because some of these refer to earlier initialized
// data via the "kernel" global variable.
//----------------------------------------------------------------------
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.
currentThread = new Thread("main", threadNum++);
currentThread->setStatus(RUNNING);
// 0 : means unused
// 1 : means used
availFrameTable = new int[NumPhysPages];
for (int i = 0; i < NumPhysPages; i++)
availFrameTable[i] = 0;
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();
#else
fileSystem = new FileSystem(formatFlag);
#endif // FILESYS_STUB
postOfficeIn = new PostOfficeInput(10);
postOfficeOut = new PostOfficeOutput(reliability);
interrupt->Enable();
}
//----------------------------------------------------------------------
// Kernel::~Kernel
// Nachos is halting. De-allocate global data structures.
//----------------------------------------------------------------------
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 availFrameTable;
Exit(0);
}
//----------------------------------------------------------------------
// Kernel::ThreadSelfTest
// Test threads, semaphores, synchlists
//----------------------------------------------------------------------
void Kernel::ThreadSelfTest()
{
Semaphore *semaphore;
SynchList<int> *synchList;
LibSelfTest(); // test library routines
currentThread->SelfTest(); // test thread switching
// test semaphore operation
semaphore = new Semaphore("test", 0);
semaphore->SelfTest();
delete semaphore;
// test locks, condition variables
// using synchronized lists
synchList = new SynchList<int>;
synchList->SelfTest(9);
delete synchList;
}
//----------------------------------------------------------------------
// Kernel::ConsoleTest
// Test the synchconsole
//----------------------------------------------------------------------
void Kernel::ConsoleTest()
{
char ch;
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!
} while (ch != EOF);
cout << "\n";
}
//----------------------------------------------------------------------
// Kernel::NetworkTest
// Test whether the post office is working. On machines #0 and #1, do:
//
// 1. send a message to the other machine at mail box #0
// 2. wait for the other machine's message to arrive (in our mailbox #0)
// 3. send an acknowledgment for the other machine's message
// 4. wait for an acknowledgement from the other machine to our
// original message
//
// This test works best if each Nachos machine has its own window
//----------------------------------------------------------------------
void Kernel::NetworkTest()
{
if (hostName == 0 || hostName == 1)
{
// if we're machine 1, send to 0 and vice versa
int farHost = (hostName == 0 ? 1 : 0);
PacketHeader outPktHdr, inPktHdr;
MailHeader outMailHdr, inMailHdr;
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;
outMailHdr.to = 0;
outMailHdr.from = 1;
outMailHdr.length = strlen(data) + 1;
// Send the first message
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.flush();
// Send acknowledgement to the other machine (using "reply to" mailbox
// in the message that just arrived
outPktHdr.to = inPktHdr.from;
outMailHdr.to = inMailHdr.from;
outMailHdr.length = strlen(ack) + 1;
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.flush();
}
// Then we're done!
}
void ForkExecute(Thread *t)
{
if (!t->space->Load(t->getName()))
{
return; // executable not found
}
t->space->Execute(t->getName());
}
void Kernel::ExecAll()
{
for (int i = 1; i <= execfileNum; i++)
{
int a = Exec(execfile[i]);
}
currentThread->Finish();
// Kernel::Exec();
}
int Kernel::Exec(char *name)
{
t[threadNum] = new Thread(name, threadNum);
t[threadNum]->space = new AddrSpace();
t[threadNum]->Fork((VoidFunctionPtr)&ForkExecute, (void *)t[threadNum]);
threadNum++;
return threadNum - 1;
/*
cout << "Total threads number is " << execfileNum << endl;
for (int n=1;n<=execfileNum;n++) {
t[n] = new Thread(execfile[n]);
t[n]->space = new AddrSpace();
t[n]->Fork((VoidFunctionPtr) &ForkExecute, (void *)t[n]);
cout << "Thread " << execfile[n] << " is executing." << endl;
}
cout << "debug Kernel::Run finished.\n";
*/
// Thread *t1 = new Thread(execfile[1]);
// Thread *t1 = new Thread("../test/test1");
// Thread *t2 = new Thread("../test/test2");
// 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);
// currentThread->Finish();
// Kernel::Run();
// cout << "after ThreadedKernel:Run();" << endl; // unreachable
}
int Kernel::allocateFrame()
{
for (int frame = 0; frame < NumPhysPages; frame++)
{
if (!availFrameTable[frame])
{
availFrameTable[frame] = 1;
return frame;
}
}
return -1;
}
```
## kernel.h
```cpp=
// kernel.h
// Global variables for the Nachos kernel.
//
// Copyright (c) 1992-1996 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.
#ifndef KERNEL_H
#define KERNEL_H
#include "copyright.h"
#include "debug.h"
#include "utility.h"
#include "thread.h"
#include "scheduler.h"
#include "interrupt.h"
#include "stats.h"
#include "alarm.h"
#include "filesys.h"
#include "machine.h"
class PostOfficeInput;
class PostOfficeOutput;
class SynchConsoleInput;
class SynchConsoleOutput;
class SynchDisk;
typedef int OpenFileId;
class Kernel {
public:
Kernel(int argc, char **argv);
// Interpret command line arguments
~Kernel(); // deallocate the kernel
void Initialize(); // initialize the kernel -- separated
// from constructor because
// refers to "kernel" as a global
void ExecAll();
int Exec(char* name);
void ThreadSelfTest(); // self test of threads and synchronization
void ConsoleTest(); // interactive console self test
void NetworkTest(); // interactive 2-machine network test
Thread* getThread(int threadID){return t[threadID];}
int allocateFrame();
void PrintInt(int number);
int CreateFile(char* filename); // fileSystem call
OpenFileId OpenFile(char* name); // fileSystem call
int WriteFile(char* buffer, int size, OpenFileId id); // fileSystem call
int ReadFile(char* buffer, int size, OpenFileId id); // fileSystem call
int CloseFile(OpenFileId id); // fileSystem call
// These are public for notational convenience; really,
// they're global variables used everywhere.
Thread *currentThread; // the thread holding the CPU
Scheduler *scheduler; // the ready list
Interrupt *interrupt; // interrupt status
Statistics *stats; // performance metrics
Alarm *alarm; // the software alarm clock
Machine *machine; // the simulated CPU
SynchConsoleInput *synchConsoleIn;
SynchConsoleOutput *synchConsoleOut;
SynchDisk *synchDisk;
FileSystem *fileSystem;
PostOfficeInput *postOfficeIn;
PostOfficeOutput *postOfficeOut;
int hostName; // machine identifier
int *availFrameTable;
private:
Thread* t[10];
char* execfile[10];
int execfileNum;
int threadNum;
bool randomSlice; // enable pseudo-random time slicing
bool debugUserProg; // single step user program
double reliability; // likelihood messages are dropped
char *consoleIn; // file to read console input from
char *consoleOut; // file to send console output to
#ifndef FILESYS_STUB
bool formatFlag; // format the disk if this is true
#endif
};
#endif // KERNEL_H
```
## AddrSpace::~AddrSpace()
```cpp=
AddrSpace::~AddrSpace()
{
for (int i = 0; i < numPages; i++)
{
kernel->availFrameTable[pageTable[i].physicalPage] = 0;
}
delete pageTable;
}
```
## bool AddrSpace::Load(char *fileName)
```cpp=
bool AddrSpace::Load(char *fileName)
{
OpenFile *executable = kernel->fileSystem->Open(fileName);
NoffHeader noffH;
unsigned int size;
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); // change file from little endian to big endian
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;
// 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
#endif
numPages = divRoundUp(size, PageSize);
size = numPages * PageSize;
ASSERT(numPages <= NumPhysPages); // check we're not trying
// to run anything too big --
// at least until we have
// virtual memory
DEBUG(dbgAddr, "Initializing address space: " << numPages << ", " << size);
pageTable = new TranslationEntry[numPages];
for (int i = 0; i < numPages; i++)
{
// physical address!=virtual address
int frame = kernel->allocateFrame();
DEBUG(dbgAddr, "Frame number" << frame);
if (frame == -1)
{
kernel->interrupt->setStatus(SystemMode);
ExceptionHandler(MemoryLimitException);
kernel->interrupt->setStatus(UserMode);
}
pageTable[i].virtualPage = i;
pageTable[i].physicalPage = frame;
pageTable[i].valid = TRUE;
pageTable[i].use = FALSE;
pageTable[i].dirty = FALSE;
pageTable[i].readOnly = FALSE;
bzero(kernel->machine->mainMemory + frame * PageSize, PageSize);
}
unsigned int physical_address;
if (noffH.code.size > 0)
{
DEBUG(dbgAddr, "Initializing code segment.");
DEBUG(dbgAddr, noffH.code.virtualAddr << ", " << noffH.code.size);
for (int capacity = noffH.code.size, offset = 0; capacity > 0; capacity -= PageSize, offset += PageSize)
{
unsigned int loadsize;
if (capacity < PageSize)
{
loadsize = capacity;
}
else
{
loadsize = PageSize;
}
// translate : virtual address,physical address, int isReadWrite
ExceptionType result = Translate(noffH.code.virtualAddr, &physical_address, 1);
// get physcial address
DEBUG(dbgAddr, physical_address << "\n");
if (result != NoException)
{
kernel->interrupt->setStatus(SystemMode);
ExceptionHandler(result);
kernel->interrupt->setStatus(UserMode);
}
executable->ReadAt(
&(kernel->machine->mainMemory[physical_address]),
loadsize, noffH.code.inFileAddr + offset);
}
}
if (noffH.initData.size > 0)
{
DEBUG(dbgAddr, "Initializing data segment.");
DEBUG(dbgAddr, noffH.initData.virtualAddr << ", " << noffH.initData.size);
for (int capacity = noffH.initData.size, offset = 0; capacity > 0; capacity -= PageSize, offset += PageSize)
{
unsigned int loadsize;
if (capacity < PageSize)
{
loadsize = capacity;
}
else
{
loadsize = PageSize;
}
// translate : virtual address,physical address, int isReadWrite
ExceptionType result = Translate(noffH.initData.virtualAddr, &physical_address, 1);
// get physcial address
DEBUG(dbgAddr, physical_address << "\n");
if (result != NoException)
{
kernel->interrupt->setStatus(SystemMode);
ExceptionHandler(result);
kernel->interrupt->setStatus(UserMode);
}
executable->ReadAt(
&(kernel->machine->mainMemory[physical_address]),
loadsize, noffH.initData.inFileAddr + offset);
}
}
#ifdef RDATA
if (noffH.readonlyData.size > 0)
{
DEBUG(dbgAddr, "Initializing read only data segment.");
DEBUG(dbgAddr, noffH.readonlyData.virtualAddr << ", " << noffH.readonlyData.size);
for (int i = noffH.readonlyData.size, pos = 0; i > 0; i -= PageSize, pos += PageSize)
{
unsigned loadsize;
i < PageSize ? loadsize = i : loadsize = PageSize;
unsigned int p = divRoundUp(noffH.readonlyData.size, PageSize);
for (int j = 0; j < p; j++)
pageTable[noffH.readonlyData.size / PageSize + j].readOnly = TRUE;
ExceptionType result = Translate(noffH.readonlyData.virtualAddr, &physical_address, false);
if (result != NoException)
{
kernel->interrupt->setStatus(SystemMode);
ExceptionHandler(result);
kernel->interrupt->setStatus(UserMode);
}
executable->ReadAt(
&(kernel->machine->mainMemory[physical_address]),
loadsize, noffH.readonlyData.inFileAddr + pos);
}
}
#endif
delete executable; // close file
return TRUE; // success
}
```
## AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
```cpp=
ExceptionType
AddrSpace::Translate(unsigned int vaddr, unsigned int *paddr, int isReadWrite)
{
TranslationEntry *pte;
int pfn;
// virtual page number
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 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->use = TRUE; // set the use, dirty bits
if (isReadWrite)
pte->dirty = TRUE;
*paddr = pfn * PageSize + offset;
if ((*paddr >= MemorySize))
{
return MemoryLimitException;
}
// ASSERT((*paddr < MemorySize));
// cerr << " -- AddrSpace::Translate(): vaddr: " << vaddr <<
// ", paddr: " << *paddr << "\n";
return NoException;
}
```