More details will be added when we study memory management and more again when we study interrupts.
1. Complete all previous instructions in f. Therefore, the only registers important for the state of f are the stack pointer (SP) and the program counter (PC)
2. Push arguments c,b,a onto P's stack. Note: Stacks usually grow downward from the top of P's segment, so pushing an item onto the stack actually involves decrementing SP.
3. Execute PUSHJ < start-address of g >. This instruction pushes PC onto the stack, and then jumps to the start address of g.
4. The first step in g is to allocate space for its own local variables by suitably decrementing SP.
g now starts its execution from the beginning. This may involve calling other procedures, possibly including recursive calls to f.
5. At the end of g: Undo step (4) and deallocate its local variables by incrementing the SP.
6. Last step of g: POPJ has the effect PC = pop(stack)
7. We are now at the step in f immediately following the call to g. Pop the arguments a,b,c off the stack and continue the execution of f.
Function f in process P calls a non-blocking system routine
1. Procedure call to library routine in user space; just as above.
2. In library routine: put system call in specified location (e.g. register)
3. Put PC in specified location LPC (e.g. register or top of user stack).
4. TRAP: Jump to location in kernel and switch to kernel mode.
5. In kernel: read system call, and go to that routine.
6. Execute body of system call. Kernel does not encounter any need to block P.
7. Jump to location saved in LPC (next step in library routine) and switch to user mode.
8. Library routine: Library procedure returns to f, as above.
Steps 1-5 are the same as for a non-blocking system call. (Necessarily, as there is no way to know whether P will block until step 6.)
6. Execute body of system call. Kernel realizes that P has to block.
7. Kernel determines that process P is running from corresponding flag in kernel.
8. Save next address for P from LPC into process table entry (PTE) for P.
9. Save SP in PTE for P.
10. Save other dynamic information in PTE for P. Some of this may be in registers, some in kernel space, some in P's user space.
11. Mark P as blocked in PTE.
12. Call the scheduler to choose a ready process Q.
13. Load Q's dynamic information from PTE for Q into wherever it goes.
14. Mark Q as the running process.
15. Jump to the next address of Q as recorderd in Q's PTE. Switch to user mode.
16. In Q: Continue.
Carry out steps 12-15 as above for P.
16. We are at the next step of the library routine in P. Return to f.
1. I/O device notifies interrupt controller.
2. Interrupt controller places index of device and event into address lines.
3. Interrupt controller issues interrupt to CPU.
4. Hardware saves PC (next address in P) in LPC, switch to kernel mode.
5. Hardware load PC from interrupt vector [ index of device,event].
6. Assembly language procedure saves registers (expressing state of P) into PTE of P.
7. Assembly language procedure sets up new stack.
8. C interrupt server runs.
Continue as in steps 7-16 of "call to blocking routine," except that P will generally be marked as "ready" rather than blocked.
131: LOAD R1, Y 132: LOAD R2, Z 133: ADD R1,R2,R3 134: STORE R3, XIf the interrupt occurs after 132 has executed, then the values of R1 and R2 need to be saved and restored before P resumes at 133. Otherwise, when P resumes at 133, R1 and R2 will contain some garbage put there by some entirely different process.
If the machine uses pipelining or a super-scalar architecture, in which several instructions are executing simultaneously, then the problem of saving state become much hairier.