System Architecture

(INFT12-212 and 72-212)
Lab Notes for Week 6: System Calls, Interrupts and Context Switching

1  Introduction

In this, the last lab on assembly programming, we are going to look at two things: how to write a system call handler, and how the CPU can context switch between two programs that wish to run.

2  A System Call Handler

We start with an example of a system call handler. Last week, we saw how to poll the Keyboard and Display Simulator device registers so that we could send and receive characters. In this example, we introduce two new system calls:
Syscall Name Purpose
104 myprint_string Like print_string, but using the Display
108 myread_string Like read_string, but using the Keyboard
Now, a user-mode program is technically not allowed to access the Keyboard and Display Simulator device registers directly, so the proper way to do I/O on this device is to create some new system calls which hide the actual I/O but provide a nice API. Therefore, we now have two new syscalls, 104 and 108, which work exactly like print_string() and read_string(), but they use the Keyboard and Display Simulator.
To demonstrate this, we need two assembly files:
  1. A user-mode program which makes the system calls.
  2. A kernel-mode system call handler which does the work.
In the following tasks, we load both files and them run them together.

2.1  Task 1

This task requires a bit of set-up, so make sure you follow the instructions below carefully.
  1. Download this file, but for now do NOT load it into the MARS simulator: wk6syscall_handler.asm.
  2. Using the instructions from lab 5, set the instruction handler to wk6syscall_handler.asm.
  3. Download this program wk6use_new_syscalls.asm, load it into MARS and assemble it.
  4. Turn on Tools -> Keyboard and Display Simulator. Connect the simulator to MARS and do a Reset, just in case.
  5. Now you should be able to run the program. The program prints a prompt string to both the Display Simulator and the MARS console. You get to type in a line of text followed by the Enter key. Finally, the program re-prints your line of text to both the Display Simulator and the MARS console.
If you want to run the program several times, it is usually a good idea to reset the Keyboard and Display Simulator each time.

2.2  Task 2

If you look at wk6use_new_syscalls.asm, it is a pretty normal user-mode program using system calls, except that some of them are repeated to show that the new syscalls work just like the old ones.
The complicated file to look at is wk6syscall_handler.asm, so we will do it in stages. The important thing to note here is that all the code so far is normal code, and it could even have been translated from a high-level language like C or Java. When we write the code for an operating system, we write as much as possible in the normal way.
The only code which has to be hand-written in assembly code is the actual system call handler, i.e. the code from handler: down. It does the same sort of thing that we saw last week: The whole point of this example is that it demonstrates that we can write our own system call handlers in MARS, but we do need to deal with the events surrounding the system call, i.e. the change from user mode to kernel mode and back again.

3  Multitasking and Context Switching

The last thing to look at is context switching, where multiple programs take turns using the CPU to run instructions. For this example, we need:
  1. Two user-mode programs. In this mini-example, both will be in the one assembly file.
  2. A source of periodic interrupts known as clock ticks, so that we can regularly get to kernel mode and make the context switch.
  3. An interrupt handler which catches the clock tick interrupts and does all the hard work.

3.1  Task 3

This task requires a bit of set-up, so make sure you follow the instructions below carefully.
  1. Download this file, but for now do NOT load it into the MARS simulator: context_switcher.asm.
  2. Using the instructions from lab 5, set the instruction handler to context_switcher.asm.
  3. Download this program wk6multitasking.asm, load it into MARS and assemble it.
  4. Lower the run speed from no interaction to 30 instructions per second.
    Figs/runspeed.png
  5. Select Tools -> Digital Lab Sim from the MARS menu bar. This is the device which will send the clock ticks.
  6. You should see a new window appear. Click the "Connect to MIPS" button in the window.
  7. Finally, run the wk6multitasking.asm program. You should see this sort of output:
    In main1, the counter is 0 
    In main1, the counter is 1 
    In main2, the counter is 10000 
    In main2, the counter is 10002 
    In main1, the counter is 2 
    In main1, the counter is 3 
    In main1, the counter is 
    In main2, the counter is 10004 
    In main2, the counter is 10006 
    In main2, the counter is 100084 
    In main1, the counter is 5 
    In main1, the counter is 6 
    In main2, the counter is 10010 
    In main2, the counter is 10012
    
    

3.2  A Look at Both Files from Task 3.

Let's now look at both of the files from the above task, to see what is going on. The wk6multitasking.asm file looks like a normal program with three functions: main(), main1() and main2().
main1() and main2() are pretty simple. Each one prints out a counter, increments it, and loops back. In fact, both of them are infinite loops. Also note that main1() never calls main2() and vice versa. But from the output above, it is quite clear that the CPU is switching between both functions, even though there is no code in this file to do that!
Even more interesting, the main() function performs a new syscall, 100, and then exit()s afterwards. So how does the CPU get to run the main1() and main2() functions, if there is no branch or jump from main()?

The answer is, of course, that we are getting the CPU to context switch between the two programs. This requires a periodic interrupt known as a clock tick to arrive. The clock tick is handled by an interrupt handler which saves the state of one program, reloads the state of the other program, and returns to the other program, i.e. not back to the program which was running when it was interrupted!

3.3  The Syscall 100 Handler

Now let's look at the code in the context_switcher.asm file.
In order to save the state of each program, we need a Process Control Block (or PCB) for each program. This has enough space to store all the registers that the program uses. In a real operating system, the PCB for MIPS would be at least 32 words in size, one for each of the 32 registers. Here, we only need space to save 5 of them:
   # Define a process control block for each program 
           .kdata 
   p1pcb:  .space 20       # Room for a0, v0, t0, t1 and PC (offsets 0, 4, 8, 12, 16) 
   p2pcb:  .space 20       # Ditto

At the top of the assembly code in this file is the generic front-end code to determine if we are dealing with a system call or an interrupt, which we are going to ignore and move down to the system call handler at the label syscallhdlr.
When we get syscall 100, the user-mode program has given us the address of the first instructions for two programs that we want to context switch between. The system call handler simply sets up initial values for all the registers for each program in its PCB. The main() program has sent us the addresses of main1() and main2(), so the PCBs are set up as follows:
Program $a0 $v0 $t0 $t1 Program Counter
1 0 0 0 0 main1()'s address
2 0 0 0 0 main2()'s address
With that done, the handler can enable the clock tick interrupts from the Digital Lab Sim window, and then jump to the code which restores the registers from the PCB and restarts one of the programs. This has the effect of starting one of the programs, and hence the syscall never returns back to the main() program!

3.4  The Clock Tick Interrupt Handler

The interrupt handler has 4 main things to do:
  1. Save all the registers into the current PCB as quickly as possible, before they are destroyed.
  2. Change over to the other PCB.
  3. Load all the registers from the new PCB back into the CPU.
  4. Do the eret instruction, which returns from kernel mode back to user mode, to the instruction that was being performed by that program when the last interrupt came in.
There's a few more things to do, like re-enabling the interrupts which were disabled when the handler started up (we don't want to be interrupted when we are dealing with an interrupt, do we?), and also resetting values in the co-processor registers, but the important actions are the 4 listed above.

Graphically, we can visualise where the CPU is executing instructions as time progresses with this diagram:
Figs/ctxtswdiag.gif

3.5  Task 4

Read through the code in both files, and make sure that you can relate the assembly instructions you see to the description above. You are not going to be expected to write your own context switcher, but you are expected to gain a good appreciation of what has to be done by the context switching code.

One question you should ask at this point is: does context switching take CPU time away from the programs? The answer is, definitely yes! There are about 30 instructions in the context switching code, and some are pseudo-instructions, so count that as probably closer to 40 real instructions. Imagine if, on average, a program got to execute 60 instructions between starting up after the eret and before the next interrupt arrived. So, out of 100 instructions, 60 were useful ones, and 40 were overhead instructions just to switch to another program. 40% of the instructions the CPU is doing are not actually running any of the programs' code. For efficiency reasons, it makes sense to make the delay between clock ticks much longer than the time taken to process them.

3.6  Task 5

Look at the output from the two programs. Can you explain why there is some output from both of the programs on the same output line, e.g.
    In main2, the counter is 10006 
    In main2, the counter is 100084 
    In main1, the counter is 5 

3.7  Task 6

Go back and rerun the program again, and see the CPU switching between the two programs. While this is happening, go into the Digital Lab Sim window and click on "Disconnect from MIPS". When the interrupt handler returns back to one of the running programs, this program will keep running indefinitely, because you have turned off the interrupts which the Digital Lab Sim window is sending into the MIPS CPU.
Now, re-enable interrupts by clicking on the "Connect to MIPS" button. The program should get interrupted, and the system will resume its original context switching behaviour.

4  Outlook for the Next Lab

In the next lab, we will begin our operating system labs.


File translated from TEX by TTH, version 3.85.
On 12 Jan 2012, 16:01.