next up previous contents
Next: Virtual Memory Up: Experience With Nachos Assignments Previous: Synchronization

Multiprogramming

  1. The Nachos Fork and Exec routines have completely different semantic from the Unix system calls. In particular, the Nachos system calls do the following:
    Exec
    Creates a new address space, reads a binary program into it, and then creates a new thread (via Thread::Fork) to run it. Note that in Unix separate fork and exec system calls are needed to achieve the same result.gif
    Fork
    Creates a new thread of control executing in an existing address space. This is a non-trivial system call to implement (for reasons discussed later).

  2. When a Nachos program issues an Exec system call, the parent process is executing the code associated with the Exec call. At some point during the Exec call, the parent will need to invoke Thread::Fork to create the new thread that executes the child process. Careful thought is required in deciding at what point the Fork operation should be done. Specifically, after Fork, the (new) child thread can no longer access variables associated with the parent process. For example, if the parent copies the name of the new file to be executed into a variable, then issues a Fork, expecting the child to open that file, a race condition exists. If the child runs after the parent, the parent may already have deleted (or changed) the variable holding that file name.

    The above race condition appears in a number of similar contexts where two processes exchange data. The problem is that the parent and child threads need to synchronize with each other, so that the parent doesn't reclaim or reuse memory before the child is finished accessing it.

  3. When the MIPS simulator ``traps'' to Nachos via the RaiseException routine, the program counter (PCReg) points point to the instruction that caused the trap. That is, the PCReg will not have been updated to point to the next instruction. This is the correct behavior. When the fault is due to (say) a page missing, for example, the faulting instruction will need be re-executed after the missing page is brought into memory. If PCReg had already been updated, there would be no way of knowing which instruction to re-execute.

    On the other hand, when a user program invokes a system call via the ``syscall'' instruction, re-executing that instruction after returning from the system call leads to an infinite system call loop. Thus, as part of executing a system call, the exception handler code in Nachos needs to update PCReg to point to the instruction following the ``syscall'' instruction. Updating PCReg is not as trivial as first seems, however, due to the possibility of delayed branches. The following code properly updates the program counter. In particular, not properly updating NextPCReg can lead to improper execution.

    	pc = machine->ReadRegister(PCReg);
    	machine->WriteRegister(PrevPCReg, pc);
    	pc = machine->ReadRegister(NextPCReg);
    	machine->WriteRegister(PCReg, pc);
    	pc += 4;
    	machine->WriteRegister(NextPCReg, pc);

    Use of the following test programs greatly helped convince me that my implementation was (finally) correct:

    ConsoleA
    Simple program that simply loops, writing the character 'A' to the console. Likewise, ConsoleB loops, printing the character 'B'.

    shell
    The provided shell starts a process and then issues a ``Join'' to wait for it to terminate. This test multiprogramming in a very rudimentary fashion only, because the shell and the invoked program rarely execute concurrently in practice.

    Modify the shell so that it can run jobs in background. For example, if the first character of the file is an ``&'', start the process as normal, but don't invoke a ``Join.'' Instead, prompt the user for the next command.

    With the modified shell, run the ConsoleA and ConsoleB programs concurrently.


next up previous contents
Next: Virtual Memory Up: Experience With Nachos Assignments Previous: Synchronization

Thomas Narten
Mon Feb 3 15:00:27 EST 1997