CSCE 451/851 Operating Systems Principles

Fall 1999
Steve Goddard

Homework 1 Solution & Notes for Problems 9, 10

  1. (20 points) Nachos is an instructional operating system that runs in a normal Unix process. Nachos will be the basis for your second and third programming projects. The purpose of these homework problems is to introduce you to the Nachos code now, before you have to make modifications to it. As such, the more you learn about Nachos now, the better off you will be later. Just as the first programming project introduced you to several new challenges, these homework problems and the last two projects will be quite challenging. Allow yourself plenty of time to complete them (in other words, start early!).

Begin by downloading your own copy of the Nachos archive. Then, follow the instructions in Building and Using Nachos.

Several documents have been created to introduce you to the Nachos system.

In this problem you will trace the execution of the Fork system call (many of the system calls in Nachos have the same name as their Unix counterpart with the first letter capitalized). The purpose of this exercise is to understand the control-flow in a Nachos system call. A subsidiary goal is to understand the finer points of an implementation of the Fork call.

There are two ways to trace the control flow in Nachos. First, you could read through the source code and note the major function calls, etc. The Nachos source is neat and well commented. As such, this is not a bad way to begin.

The second way is to use a debugger to step through the execution of the operating system. For the purposes of this assignment, we have provided a pre-compiled version of the user program below. You can use the SmartGDB debugger to step through Nachos while running it. This way involves more initial work in learning the debugger, but may assist in getting a general idea of what's going on faster than diving right into the code.

Given the following Nachos user program:

int main( ) {
int foo;
Fork();
foo = 1;
}

Trace the execution of the program through the Nachos system from when it begins to execute the Fork call to the return of the main procedure.

Include the answers to the following questions with your trace:

    1. What do the MIPS registers look like upon entry into the Nachos kernel?
    2. When is a new process actually created? What are the steps involved in creating it?
    3. Where does the new process start execution inside the kernel? Inside user space?
    4. What functions are responsible for

    1. What is the relationship of the two processes' memory spaces after the fork?
    2. How does the assignment of foo change the relationship between the memory spaces of the two threads after the fork?

We have done this using SmartGDB and a TCL script that you can download and try yourself (if you have problems downloading the TCL script, click on the link with your right mouse button and choose "Save Link As" from the menu that pops up). Due to the size of the output, it is available on a separate page.

  1. On entry to the Nachos kernel, MIPS register 2 always contains the number of the system call to be invoked (in this case, SC_Fork or 9). Since Fork() takes no arguments, registers 4, 5, 6, and 7 will be undefined.
  2. The new process is actually created when the new Thread object is created. It is not set up for execution until Thread::Fork is called. The following steps are involved in creating the new process:

    1. Create a new process, Thread object
    2. Create a new address space, AddrSpace object
    3. Copy the current process' address space into the new one, AddrSpace::CopyFrom
    4. Establish the parent of the new process, Thread::Set_Parent_Ptr
    5. Copy the user registers to the new process, Thread::Write_Reg
    6. Fork the new process to set up its stack, Thread::Fork
    7. Add the new process as a child of the current process, Thread::Add_Child
    8. Place the new process in the ready list, Scheduler::ReadyToRun

  1. The new process begins execution inside the kernel in the Do_Fork function. It begins execution in the user process immediately after the Fork system call.
  2. The calls to Thread::Write_Reg set up the registers for the new process. The call to Machine::Fix_Fork_Registers finishes cleaning up the registers by inserting 0 into register 2 (the return value from Fork) and doing the steps that would have happened at the end of the call to Machine::Run had it returned to that function like the parent process did. The call to AddrSpace::CopyFrom makes the copy of the current process's address space.
  3. The simple answer is that the two process' memory spaces are identical after the Fork. The only difference between the two processes lies in their registers--the child has a return value of 0 in register 2 and the parent has the child's PID in register 2.
  4. The more complex answer involves understanding why the two process' memory spaces are identical. The AddrSpace::CopyFrom function allocates a new page table consisting of a TranslationEntry for each virtual page of memory the parent process has. It then sets up each TranslationEntry so that it points to the same physical page that the parent is using. Finally, it sets the Copy-On-Write (COW) flag in its TranslationEntry and the parent's. The Copy-On-Write flag indicates that whenever a process tries to write to the page that it must first make a new physical copy of the page (in a new frame in physical memory), reset its TranslationEntry to point to the new page and then clear the COW flag for its page table entry and the parent's.

  5. This involves understanding the complex answer in the previous point. When one of the processes tries to assign a value to foo after Fork (in Machine::Translate, it sees that its COW flag is set for that page. It must then make a new copy (using AddrSpace::duplicatePage) of the physical page it is sharing with its parent before it modifies the page. After making a new copy of the page and modifying both page table entries (its own and the other process sharing the page), it is then allowed to continue execution and modify the variable. Note that it is possible for more than two processes to be sharing a single page which makes the algorithm more complex.

  1. (20 points) The point of this question is to make sure you really understand the concept of context switching. Write down the sequence of procedure calls and returns, along with changes to the ready list, that would occur in Nachos if the program below were to execute. For each procedure call/return, give the name of the thread doing the call/return, the full procedure name, and the values of any significant arguments. Assume the initial thread is called Thread1 and the thread that is forked is called Thread2.

For this question you only have to list calls and returns to major routines defined in thread.cc, scheduler.cc, switch.s, exception.cc, mipssim.cc, machine.cc, and systemcall.cc along with calls to Interrupt::SetLevel( ). In other words, you can ignore ASSERTs, DEBUG statements, calls to list routines, calls made internal to the interrupt simulation, any trivial inline functions, etc.

For example, your solution might begin:

RaiseException: Thread1 has raised an exception (SyscallException)
ExceptionHandler: Thread1 doing a system call (SyscallException)
do_system_call: Thread1 entering switch (SC_Fork)
...

You should assume for the purposes of this excercise that the following are true:

    1. Interrupts are disabled (i.e. there is NO preemption). This is accomplished by using the '-noswitch' option when running Nachos. If you trace the actual execution of Nachos, make sure you add the '-noswitch' option when starting Nachos
    2. The ready queue is empty except for the threads running the user code below.

It is acceptable (even encouraged) to generate your solution automatically (either by code modifications or by SmartGDB scripts).

int main( ) {
Fork( );
Yield( );
}

We have done this using SmartGDB and a TCL script that you can download and try yourself (if you have problems downloading the TCL script, click on the link with your right mouse button and choose "Save Link As" from the menu that pops up). Due to the size of the output, it is available on a separate page.