Lab 4: Multiprogramming
Computer Science 377
Due: Friday, April 30, 1:00 PM

(VIP students have 2 weeks from when you receive it.)

Please start immediately on this assignment. You will not be able to complete it in a single day.

The third phase of Nachos is to implement multiprogramming. As in the first two assignments, we give you some of the code you need; your job is to complete the system and enhance it. This document first describes the features in Nachos that you need to focus on for this assignment.

Understanding the Nachos Software, Part III

Before attempting to understand this assignment, you must first understand the parts of Nachos that you will need to work with. The following will be extremely helpful in understanding this assignment:

You will probably also find the following helpful:

Once you understand the relevant portions of Nachos, read the remainder of this handout in its entirety before beginning your project.

The Assignment

In this assignment, you will implement multiprogramming for Nachos. As in the last lab, you will test your implementation by running user programs on Nachos. At present, Nachos can run a single user-level C program at a time. For this lab, you will start a user program by running Nachos with the "-x" flag as for the last lab. From the application program perspective, multiprogramming is accomplished by having a C program "exec" other programs. That is, there is no user shell where multiple application programs are started.

In the assignment, we tell you to put output statements in your code at various places. To do this, use the DEBUG procedure call, passing '4' as the first argument, as in:

DEBUG ('4', "Allocating frame %d.\n", frame);

To see this output run nachos with the -d 4 flag. This should facilitate debugging on your part and will also facilitate testing and grading. These statements must be present to receive full credit for a portion of the assignment. Feel free to add additional statements that you feel help clarify how your code is working. (Error messages that must be reported to the nachos user, such as when a system call is given bad parameter values, should be output with normal printf statements so that they always appear.)

Before beginning this assignment, do the following:

  1. In machine/machine.h, change
    #define NumPhysPages    32
    
    to
    #define NumPhysPages    48
    
  2. In vm/Makefile, delete -DUSE_TLB from the line:

    DEFINES = -DUSER_PROGRAM -DFILESYS_NEEDED -DFILESYS_STUB -DVM -DUSE_TLB

  3. Rebuild the makefile (gmake depend in the vm directory.

  4. Recompile nachos (gmake nachos in the vm directory.
To implement multi-programming, you must:

  1. (20 pts.) Implement an object to keep track of free pages. It's an instance of BitMap (from bitmap.h). The bitmap must be updated whenever a process starts or stops. Ignore the use and dirty bits since we are not implementing demand paging. Change the addrspace constructor to initialize the page table by looking for free page frames; currently it assumes they are all free. Output a DEBUG statement on each page allocation and deallocation identifying the frame number.

  2. (20 pts.) Implement a memory management scheme that does paging. You should load a process's entire address space into physical memory when the process is started. You do not need to implement demand paging. If there is not enough memory to meet a process's needs, you can simply display an error message and abort the process. Remember to free the memory when the process exits. Output a DEBUG message for each page loaded into memory identifying the virtual page number and the physical page frame.

    • Tip: Currently, Nachos zeros the entire main memory when an address space is created using a call to bzero. You need to be sure that you only zero the pages actually allocated to that process.

  3. (20 pts.) Implement the Exec() system call. Exec is quite similar to StartProcess in userprog/progtest.cc. Output a DEBUG message at the beginning and end of Exec identifying it as an Exec call. In the end message, identify the id of the new process created. This is the value that Exec will return.

    • Tip: The Exec system call should not actually start the new process. It should create all the necessary data structures and then call Fork (like in producer/consumer and barber). Here is the procedure that you should send as the first parameter to Fork:
      //----------------------------------------------------------------------
      // ExecProcess
      // 	This is called by SyscallExec to actually run the new process.
      //	It is a forked kernel thread which begins by setting up it's
      //	address space and initializes things.  It then calls machine->Run
      //	which causes it to execute.
      //
      //----------------------------------------------------------------------
      
      
      void ExecProcess(int execFileName)
      {
        DEBUG('4', "Starting process %s\n", (char *) execFileName);
        currentThread->space->InitRegisters();
        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"
      }
      
  4. (20 pts.) Implement the Join() system call. Join results in a process waiting for another specific process to finish. Join puts itself to sleep to wait for the process whose id is passed in to complete. Nachos must be able to map from SpaceIds to threads, much as it maps from OpenFileIds to files. Output a DEBUG message at the beginning of Join identifying it as a Join. Also, identify the process id of the current thread and the id of the thread it wants to wait for. When a process waits for another process, indicate this with a DEBUG message before going to sleep. Before Join returns, output another DEBUG message indicating which process is continuing.

  5. (10 pts.) Make whatever changes are necessary to your system call implementations. For example, if you used a global file table, you must now turn that into a per-process file table. (You do not need to implement the file pointer. You can let the underlying Unix implementation of the file system manage that for you.) Also, if you implemented Exit as a call Halt in the last lab, you need to change this. You should only halt Nachos if there are no more processes alive.

  6. (5 pts.) Use semaphores to provide synchronized access to the SynchConsole class you implemented in the last lab so that multiple processes can read and write from/to the console. Essentially, you should prevent two threads from executing PutChar concurrently or GetChar concurrently.

  7. (5 pts.) Implement a timer so that each user process gives up the CPU after its timeslice expires. In threads/system.cc, there is currently code to create a timer if the program is called with the -rs flag. You just need to change this so that the timer is always created. All other aspects of the timer are already implemented. (The length of the time slice is defined by TimerTicks defined in machine/stats.h.)

Most of your work will again be in the userprog directory. Here are the files you will need to read/understand to complete this assignment.

progtest.cc (found in the userprog directory)
Test routines for running user programs. Look at StartProcess to help understand what you need to do to implement the Exec system call.

addrspace.h, addrspace.cc (found in the userprog directory)
Create and destroy address spaces for user programs. You will need to change the address space class to support page tables.

syscall.h (found in the userprog directory)
the system call interface. This file contains the signatures of the system calls you must implement and the codes that represent each system call when ExceptionHandler is called.

exception.cc (found in the userprog directory)
The handler for system calls. You need to implement the Exec and Join calls in this lab.

machine.h (found in the machine directory)
Emulates the part of the machine that executes user programs: main memory, processor registers, etc. Defines useful constants like PageSize, NumPhysPages, and MemorySize. Provides the routines to read and write values from/to memory.

main.cc (found in the threads directory)
The main program. For this lab, main.cc is compiled with the USER_PROGRAM and VM flags set. (The Makefile in vm causes this to happen.)

system.h, system.cc (found in the threads directory)
The file that initializes global variables when Nachos starts and cleans up before Nachos halts. If you add any new global variables, they should be declared in system.h and initialized in the Initialize method in system.cc. Of particular interest for this assignment is the timer variable.

bitmap.h (found in the threads directory)
Defines a BitMap class which will be useful for keeping track of free frames.

timer.h (found in the machine directory)
Defines a Timer class which implements time slices.

As in the last lab, you will need to "bullet-proof" the Nachos kernel from user program errors -- there should be nothing a user program can do to crash the operating system (with the exception of explicitly asking the system to halt). Therefore, your system call implementations should do whatever error checking is necessary to ensure that they will succeed or will cause the user's program to fail, but not nachos.

How to Turn in Lab 4

All of the following files and hard copies must be turned in to get full credit for a question.

  1. Hand in a hard copy of all the files that you modified. Remember to mark your changes:
    // ---------------- Start changes -----------------------------------
       put your changes here
    // ---------------- End changes -------------------------------------
    
    You should put all your modified files in a lab4 subdirectory of your cs377 directory.

  2. Your nachos executable should exist in the vm directory.

  3. Hand in a hard copy of a README file that explains your implementation for each part of the assignment. README should contain:

    Your ~/cs377/lab4 directory should also contain the README file.

Testing MultiProgramming Using Exec and Join

Here are some test programs for you to use. You should make up your own test programs to more thoroughly test your code.