CSC546 - Operating Systems


Topics

static char arrays

OpenFile (again)

Console I/O

Nachos (C++) Semaphore class

Virtual Memory

BitMap

Adding a C++ class to Nachos

CoreMap

Exceptions: machine/machine.h

PageFaultException


Static Character Arrays

C compilers sometimes place restrictions on declarations of character arrays which also include initialization:

char msg[] = "Hello, World!\n";

The restriction is that such a declaration with initialization, must either be (1) a global declaration or (2) must be declared static if local to a function:

#include "syscall.h"
int main()
{ 
  static char msg[] = "Hello, World!\n";

  Write(msg, 13, 1);
 ...
}

OpenFile

The destructor for OpenFile closes the associated file.

This is a problem if two nachos threads are sharing the file.

When nachos begins (executing main), it is set up as a nachos thread. This allows context switches between the initial thread executing main and any other threads that are created.

This means that a Thread object is created even for the execution of nachos main function.

See the function Initialize(...) which is called from main and is implemented in threads/system.cc.


Initialize(argc, argv)

    stats = new Statistics();                   // collect statistics
    interrupt = new Interrupt;                  // start up interrupt handling
    scheduler = new Scheduler();                // initialize the ready queue
    if (randomYield)                            // start the timer (if needed)
        timer = new Timer(TimerInterruptHandler, 0, randomYield);

    threadToBeDestroyed = NULL;

    // 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");
    currentThread->setStatus(RUNNING);

    interrupt->Enable();
    CallOnUserAbort(Cleanup);                   // if user hits ctl-C

#ifdef USER_PROGRAM
    machine = new Machine(debugUserProg);       // this must come first
#endif

Summary: Don't Close Standard Input/Output

All nachos threads (including the initial nachos kernel thread executing main) share standard input/output.

Normal output and debugging messages from Nachos will not appear if some nachos user thread closes standard output.

Alternatives?


Console I/O

The userprog/nachos implements the -c option. This option tests the Console class whic represents keyboard input, terminal output.

The console test (with the -c option) is run by:

hawk% cd nachos-3.4/code/userprog
hawk% nachos -c
Hello  (Type in this input.)
Hello  (Result printed to terminal screen.)
q      ( or anything that begins with q )
qMachine halting!

Ticks: total 5195530, idle 5195380, system 150, user 0
Disk I/O: reads 0, writes 0
Console I/O: reads 8, writes 7
Paging: faults 0
Network I/O: packets received 0, sent 0

Cleaning up...
hawk%  

Console and Nachos (C++) Semaphores

The Console simulation is based on semaphores.


static Console *console;
static Semaphore *readAvail;
static Semaphore *writeDone;

static void ReadAvail(int arg) { readAvail->V(); }
static void WriteDone(int arg) { writeDone->V(); }

ConsoleTest (char *in, char *out)
{
    char ch;

    console = new Console(in, out, ReadAvail, WriteDone, 0);
    readAvail = new Semaphore("read avail", 0);
    writeDone = new Semaphore("write done", 0);

    for (;;) {
        readAvail->P();         // wait for character to arrive
        ch = console->GetChar();
        console->PutChar(ch);   // echo it!
        writeDone->P() ;        // wait for write to finish
        if (ch == 'q') return;  // if q, quit
    }
}

Note that readAvail and writeDone are "Semaphore" objects each with initial value 0. The Semaphore::P() and Semaphore::V() operations are provided by the nachos kernel in the C++ Semaphore class.

P is for "decrement" and V is for "increment".

The semaphore value is not allowed to be < 0. And so the Semaphore::P operation will block the calling nachos thread if the integer value of the Semaphore is already 0.


Semaphore::P() and Semaphore::V()

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);   // disable interrupts

    while (value == 0) {                        // semaphore not available
        queue->Append((void *)currentThread);   // so go to sleep
        currentThread->Sleep();
    }
    value--;                                    // semaphore available,
                                                // consume its value

    (void) interrupt->SetLevel(oldLevel);       // re-enable interrupts
}
void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *)queue->Remove();
    if (thread != NULL)    // make thread ready, consuming the V immediately
        scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}

Virtual Memory

We need at least to be able to load two programs into Mips memory in order to have any implementation of the Nachos Exec system call.

From syscall.h:

/* Run the executable, stored in the Nachos file "name", and return the
 * address space identifier
 */
SpaceId Exec(char *name);

BitMap

The BitMap class is defined in userprog/bitmap.h:

class BitMap {
  public:
    BitMap(int nitems);         // Initialize a bitmap, with "nitems" bits
                                // initially, all bits are cleared.
    ~BitMap();                  // De-allocate bitmap

    void Mark(int which);       // Set the "nth" bit
    void Clear(int which);      // Clear the "nth" bit
    bool Test(int which);       // Is the "nth" bit set?
    int Find();                 // Return the # of a clear bit, and as a side
                                // effect, set the bit.
                                // If no bits are clear, return -1.
    int NumClear();             // Return the number of clear bits

    void Print();               // Print contents of bitmap

    // These aren't needed until FILESYS, when we will need to read and
    // write the bitmap to a file
    void FetchFrom(OpenFile *file);     // fetch contents from disk
    void WriteBack(OpenFile *file);     // write contents to disk

  private:
    int numBits;                        // number of bits in the bitmap
    int numWords;                       // number of words of bitmap storage
                                        // (rounded up if numBits is not a
                                        //  multiple of the number of bits in
                                        //  a word)
    unsigned int *map;                  // bit storage
};

StartProcess and BitMap

The nachos routine, StartProcess in userprog/progtest.cc, loads a user program in memory. It could be modified to use BitMap as follows:

   numPages = divRoundUp(size, PageSize);


//    ASSERT(numPages <= NumPhysPages);         // check we're not trying
                                                // allocate more than
                                                // physical memory at this
                                                // point with partial loading
                                                // of pages not yet
                                                // implemented.


    // Are there enough free frames for the initial load?
    DEBUG('b', "free frames = %d, need %d for %s\n",
          frameMap->NumClear(), numPages, filename);
    if ( frameMap->NumClear() < numPages )
      {
        printf("Not enough physical memory to start user program!\n");

However, suppose the BitMap class doesn't quite provide enough functionality. You could create a derived class, say CoreMap.


Adding a C++ Class to Nachos

How do we add a new class to Nachos and still have the makefiles work?

Suppose we decide to create a new class CoreMap, derived from BitMap.

Steps:

  1. bitmap.h is in the userprog directory. Create coremap.h in the same directory.
  2. Create coremap.cc, the implementation file, in the same directory also.
  3. In the Makefile.common file in the code directory make the following changes:
    1. Add coremap.h to the USERPROG_H definition:
      USERPROG_H = ../userprog/addrspace.h\
              ../userprog/bitmap.h\
              ../userprog/coremap.h\
              ../filesys/filesys.h\
              ../filesys/openfile.h\
              ../machine/console.h\
              ../machine/machine.h\
              ../machine/mipssim.h\
              ../machine/translate.h
      
    2. Add coredmap.cc to the USERPROG_C definition
      USERPROG_C = ../userprog/addrspace.cc\
              ../userprog/bitmap.cc\
              ../userprog/coremap.cc\
              ../userprog/exception.cc\
              ../userprog/progtest.cc\
              ../machine/console.cc\
              ../machine/machine.cc\
              ../machine/mipssim.cc\
              ../machine/translate.cc
      
    3. Add coremap.o to the USERPROG_O definition:
      ../userprog/coremap.cc\
              ../userprog/exception.cc\
              ../userprog/progtest.cc\
              ../machine/console.cc\
              ../machine/machine.cc\
              ../machine/mipssim.cc\
              ../machine/translate.cc
      
    4. Add coremap.o to the USERPROG_O definition:
      USERPROG_O = addrspace.o bitmap.o coremap.o exception.o progtest.o console.o \
              machine.o mipssim.o translate.o
      
      
  4. Change to the code directory and execute:
    make all
    

Note the \ escapes the newline and is very important.


Exceptions

Remember, all the exceptions have symbolic names and are listed in machine/machine.h

One particular such exception is PageFaultException.

Look at the page table again.