Nachos - SynchronizationProcesses sometimes (in fact frequently) need to communicate with other processes. There needs to be a well-structured way for this communication to occur which does not rely on interrupts. There needs to be a well defined way of passing information between processes, coordinating process activity. There are three high level synchronization techniques which provide Synchronization. They are Semaphores, locks and Monitors & condition variables.
To achieve the desired effect, we can view teh semaphore as a variable that has an integer value upon which three operations are defined:
The wait and signal
primitives are assumed to be atomic; that is, they cannot be interrupted
and each routine can be treated as an indivisible step. A queue is used
to hold processes waiting on the semaphore usually FIFO.
A Definition of Semaphore primitives
war s: semaphore;
wait(s):
s.count:= s.count - 1;
if s.count < 0
then begin
place this process in s.queue;
block this process
end;
signal(s):
s.count:= s.count + 1;
if s.count <= 0
then begin
remove a process P from s.queue;
place process P on ready list
end;
The testing and modification of the value of the semaphore in both the operations must be done atomically, i.e., when one process is testing or modifying the semaphore value, no other process can modify the value. To see how these operations can be used to synchronize two processes, assume two processes A and B, executing statements S1 and S2 respectively. The condition is that S2 can be executed only after S1.
A lock is used to support mutual exclusion principle. To support the mutual exclusion there are two operations provided which are Acquire() and Require(). When a process wants to enter critical section of a shared variable, it has to Acquire the lock. After the work with the shared variable is over it has to Release the lock. The lock can be in two modes, free or busy. Initially all locks are free. When a process acquires a lock when it is free. When a lock is held by a process then the lock is said to be busy. Any process that attempts to enter its critical section while another process is in its critical section, is made to wait because the lock is busy. The code executed during this stage is as follows:
Lock -> Acquire();
< critical section >
Lock -> Release();
Another high level sychronisation construct is the monitor type. A monitor is characterized by a set of programmer-defined operators. The representation of a monitor type consists of declarations of variables whose values define the state of an instance of the type, as well as the bodies of procedures or functions that implement operations on the type. The syntax of a monitor is
type monitor-name = monitor
variable declarations
procedure entry P1(..);
begin .. end;
..
..
begin
initialization code
end
By enforcing the principle of one process at a time, the monitor is able to provide a mutual exclusion facility. The data variables in the monitor can be accessed by only one process at a time. Thus, a shared data structure can be protected by placing it in a monitor. If the data in a monitor represent some resource, then the monitor provides mutual exclusion facility for accessing the resource. By turning all the critical region procedures into monitor mode, no two procedure will ever execute their critical regions at the same time.
A monitor supports synchronization by the use of condition variables that are contained within the monitor and accessible only within the monitor. Two functions operate on condition variables:
Note that monitor wait/signal operations are different from those for the semaphore. If a process in monitor signals and no task is waiting on the condition variable, the signal is lost.