Traps ----- Traps occur if an error occurs during an instruction execution, or if a TRAP instruction is executed. They behave very like interrupts, and are often called software interrupts. When a trap occurs, the contents of the PSW and PC are saved on the stack, and are replaced by the values in the trap's TRAP VECTOR. Error traps. Error traps are executed at a higher priority than interrupts. They occur during power failure, odd addressing errors (when a program attempts to execute an instruction on an odd address), stack errors (when a stack overflow is imminent (yellow zone violation), or has actually occurred (red zone violation)), timeout errors (when a non-existent peripheral is accessed), non- existent memory errors, memory management violations (when an attempt is made to access a portion of memory which should not be accessed by the process), memory parity errors, and use of privileged instructions in user mode. Program traps. Program traps occur when any of EMT, TRAP, BPT or IOT instructions are executed. EMT is used in system software, TRAP is available for programmer use, BPT is used to call debugging aids, and IOT is used to call certain I/O routines. EMT and TRAP instructions take a parameter in the range 0 - 377 octal. The EMT instruction is used to call routines inside the operating system's kernel. Up to 256 different routines are possible. Which one is called depends on the parameter supplied with the instruction. When the EMT instruction is called, the program counter is taken from the word at address 30. The following code starts at the address held in address 30. It contains an index into a switch table (a switch table is similar to Pascal's CASE statement or BASIC's ON ... GOTO ... statement). EMTENT: MOV (SP),R0 ;Get PC from top of stack. MOV -(R0),R0 ;Put EMT instruction into R0. BIC #177400,R0 ;Clear EMT op code leaving ;parameter in bottom byte. BR @SWTB(R0) ;Jump to address in switch ;table depending on param. SWTB: .WORD EMT000 ;Routine 0. .WORD EMT002 ;Routine 2. . . . . . . The system macros (e.g. .TTYIN, .TTYOUT and .EXIT) are implemented using the EMT instruction, each particular system macro call taking a different parameter. Concurrent Processes -------------------- On a single processor, only one process can execute at any given time. Most operating systems, however, give the user the appearance of enabling the processor to execute several processes concurrently. This can be acheived on a single processor by regularly switching between processes every fraction of a second, so that, over a span of a few seconds, each of the processes executing gets a share of the processor's time. The effect of this is that the processes appears to be executing concurrently, and each process is somewhere between the starting point of its execution and its end point. The Dispatcher The dispatcher (or low-level scheduler) allocates the processor among the various processes. It is entered whenever an interrupt changes the status of a process, a call is made to the operating system's kernel, or an error trap has occurred. In effect, the dispatcher is entered after any interrupt (hardware or software) has occurred. The dispatcher operates as follows: IF the current process is still the most suitable to run THEN return control to the point at which it was interrupted ELSE save the volatile environment (register contents, etc.) of the current process; restore the environment of the most suitable process to run; transfer control to the restored program at the point at which it was suspended END IF; The most suitable process to run is the one with the highest priority. Priorities are determined by a higher level operating system routine called the scheduler, and given to the dispatcher. Each process is known to the dispatcher through its process descriptor, and contains (among other information) the state of the process (running, runnable, not runnable) and its priority (though this may be represented by the descriptor's position in the queue). Inter-process Communication and Semaphores Processes need to communicate in order to coordinate the use of resources. The method normally used for inter-process communication is the semaphore. A semaphore is a non-negative integer whose value indicates the availability of a resource, the number of processes allowed to use the resource simultaneously being equal to the initial value of the semaphore. Each process which uses the resource is able to alter the value of the semaphore, by means of the operations WAIT and SIGNAL. When WAIT is executed, the value of the semaphore is decremented by one, unless its value is already zero. If the semaphore has value zero, the process executing is suspended and another process starts execution. The suspended process can only start executing again when the semaphore has a value greater than zero. The WAIT operation could then be implemented as Test the value of the semaphore. Suspend execution if its value is zero. Otherwise, decrement the value of the semaphore. There is a problem with this, however. A process A might test the value of the semaphore and find that it is 1. It might then be interrupted, and a process B executed. If B tests the same semaphore, it will also find that it is valued 1. It would then decrement the semaphore, so that its new value is zero. Process B might then be interrupted, and process A resume. Process A, having found that the value of the semaphore had value 1, will then decrement the semaphore. The value of the semaphore would then become -1. This should not occur. It can be prevented if interrupts are disabled at the start of the WAIT operation, and enabled again when the operation is completed. When SIGNAL is executed, the value of the semaphore is incremented by one. SIGNAL must also be able to execute without being interrupted. A process suspended on a WAIT can be activated only after a SIGNAL operation has been performed on the semaphore. Processes must communicate so that they avoid simultaneous use of a non-shareable resource, and in order to synchronise their operations. In acheiving these aims, they must also avoid deadlock, when neither of two processes can continue execution without the resource held by the other. 1) Mutual Exclusion. Simultaneous access to non-shareable resources is prevented by preventing concurrent execution of the section of code through which the access is made (such sections of code are called CRITICAL SECTIONS). This is done by initialising the semaphore which controls the use of the resource to one, and executing a WAIT at the start of the section of code, and a SIGNAL at the end. At the start of execution of a critical section, the semaphore gets the value zero, and any other processes which attempt to execute the critical section are suspended until the process executing it has completed and restored the semaphore value to 1. 2) Synchronisation. It is often necessary to hold up the operation of one process until a second process has reached a certain point. This can be done by including a WAIT in the first process, and a SIGNAL in the second, and initialising a semaphore to zero. When the first process reaches WAIT, it is suspended until the second process has executed SIGNAL on the semaphore. Avoidance of Deadlock. If two processes each require the use of a resource held by the other, then neither process can proceed. This situation will occur if the following code is executed. Process 1. Process 2. . . . . WAIT on semaphore X WAIT on semaphore Y . . . . WAIT in semaphore Y WAIT on semaphore X . . If semaphores X and Y are both initially set to 1, and both processes execute their first WAIT, then each process will be suspended on its second WAIT. Neither process can then continue unless a third process signals either semaphore X or semaphore Y. If no such third process exists, then neither of the two processes can continue execution. Implementation of WAIT and SIGNAL --------------------------------- WAIT and SIGNAL can be implemented as program traps, using the EMT instruction with particular parameters. The trap vector's PSW contains 7 in its priority field, ensuring that the trap is not interrupted. This means that the WAIT and SIGNAL operations cannot be interrupted, as required. Semaphores are actually implemented with a non-negative integer, and a pointer into a queue. This queue contains the processes which are waiting on the semaphore. The WAIT and SIGNAL routines now are implemented as follows: procedure wait(s); begin if s > 0 then s := s - 1 else add process to semaphore queue; modify process descriptor to make process unrunnable end if end; procedure signal(s); begin if s.queue is empty then s := s + 1 else remove a process from the semaphore queue; modify process descriptor to make process runnable end if end; The details of the queues are implementation dependent.