Process Synchronization Notes
Process Synchronization Notes
BACKGROUND
1
CS4TH3 OPERATING SYSTEMS
2
CS4TH3 OPERATING SYSTEMS
Consider a system consisting of n processes {P0, P1,..., Pn-1}. Each process has a
segment of code, called a critical section, in which the process may be changing common
variables, updating a table, writing a file, and so on. The important feature of the system is
that, when one process is executing in its critical section, no other process is to be allowed
to execute in its critical section.
3
CS4TH3 OPERATING SYSTEMS
That is, no two processes are executing in their critical sections at the same time.
The critical-section problem is to design a protocol that the processes can use to cooperate.
Each process must request permission to enter its critical section. The section of code
implementing this request is the entry section. The critical section may be followed by an
exit section. The remaining code is the remainder section. The general structure of a
typical process Pi, is shown in the following figure:
A solution to the critical-section problem must satisfy the following three requirements:
4
CS4TH3 OPERATING SYSTEMS
yields control of the CPU. Obviously, a nonpreemptive kernel is essentially free from race
conditions on kernel data structures, as only one process is active in the kernel at a time.
We cannot say the same about nonpreemptive kernels, so they must be carefully
designed to ensure that shared kernel data are free from race conditions. Preemptive kernels
are especially difficult to design for SMP architectures, since in these environments it is
possible for two kernel-mode processes to run simultaneously on different processors.
A preemptive kernel is more suitable for real-time programming, as it will allow a
real-time process to preempt a process currently running in the kernel. Furthermore, a
preemptive kernel may be more responsive, since there is less risk that a kernel-mode
process will run for an arbitrarily long period before relinquishing the processor to waiting
processes.
5
CS4TH3 OPERATING SYSTEMS
6
CS4TH3 OPERATING SYSTEMS
SYNCHRONIZATION HARDWARE
7
CS4TH3 OPERATING SYSTEMS
8
CS4TH3 OPERATING SYSTEMS
9
CS4TH3 OPERATING SYSTEMS
10
CS4TH3 OPERATING SYSTEMS
SEMAPHORES
11
CS4TH3 OPERATING SYSTEMS
wait(S) {
while S <= 0
; // no-op S--;
}
signal(S) {
S++;
}
All the modifications to the integer value of the semaphore in the wait () and signal
() operations must be executed indivisibly. That is, when one process modifies the
semaphore value, no other process can simultaneously modify that same semaphore value.
In addition, in the case of wait(S), the testing of the integer value of S (S < 0), and its
possible modification (S—), must also be executed without interruption.
Operating systems often distinguish between counting and binary semaphores. The
value of a counting semaphore can range over an unrestricted domain. The value of a binary
semaphore can range only between 0 and 1. On some systems, binary semaphores are
known as mutex locks, as they are locks that provide mutual exclusion.
We can use binary semaphores to deal with the critical-section problem for multiple
processes. The n processes share a semaphore, mutex, initialized to 1. Each process Pi is
organized as shown:
do {
waiting(mutex);
// critical section signal
(mutex) ,-
// remainder section
}while (TRUE);
12
CS4TH3 OPERATING SYSTEMS
We can also use semaphores to solve various synchronization problems. For example,
consider two concurrently running processes: P1 with a statement S1 and P2 with a
statement S2. Suppose we require that P2 be executed only after S1 has completed. We can
implement this scheme readily by letting P1 and P2 share a common semaphore synch,
initialized to 0, and by inserting the statements.
S1;
signal(synch);
wait(synch);
S2;
in process P2. Because synch is initialized to 0, P2 will execute S2 only after P1 has
invoked signal (synch), which is after statement S1 has been executed.
Semaphore Implementation:
The main disadvantage of the mutual-exclusion solutions and of the semaphore definition given
here, isthat they all require busy waiting. While a process is in its critical section, any other process
that tries to enter its critical section must loop continuously in the entry code. This continual
looping is clearly a problem in a real multiprogramming system, where a single CPU is shared
among many processes. Busy waiting wastes CPU cycles that some other process might be able to
use productively. This type of semaphore is also called a spinlock (because the process "spins"
while waiting for the lock). Spinlocks are useful in multiprocessor systems. The advantage of a
spinlock is that no context switch is required when a process must wait on a lock, and a context
switch may take considerable time. Thus, when locks are expected to be held for short times,
spinlocks are useful.
To overcome the need for busy waiting, we can modify the definition of the wait and signal
semaphore operations. When a process executes the wait operation and finds that the semaphore
value is not positive, it must wait. However, rather than busy waiting, the process can block itself.
The block operation places a process into a waiting queue associated with the semaphore, and the
state of the process is switched to the waiting state. Then, control is transferred to the CPU
scheduler, which selects another process to execute.
A process that is blocked, waiting on a semaphore S, should be restarted when some other
process executes a signal operation. The process is restarted by a wakeup operation, which changes
the process from the waiting state to the ready state. The process is then placed in the ready queue.
(The CPU may or may not be switched from the running process to the newly ready process,
depending on the CPU-scheduling algorithm.)
To implement semaphores under this definition, we define a semaphore
13
CS4TH3 OPERATING SYSTEMS
as a "C" struct:
typedef struct {
int value ;
struct process *L;
) semaphore;
Each semaphore has an integer value and a list of processes. When a process must wait on a
semaphore, it is added to the list of processes. A signal operation removes one process from the
list of waiting processes and awakens that process.
The wait semaphore operation can now be defined as
void
wait(semaphore
S) {S.value--;
if (S.value < 0) {
add this process
to S . L; block()
;
}
The signal semaphore operation can now be
defined as void signal(semaphore S) {
S.value++;
if (S.value <= 0) {
remove a process P
from S . L ; wakeup
(PI) ;
}
The block operation suspends the process that invokes it. The wakeup(P1) operation resumes the
execution of ablocked process P. These two operations are provided by the operating system as
basic system calls.
Binary Semaphores
The semaphore construct described in the previous sections is commonly known as a counting
semaphore, since its integer value can range over an unrestricted domain. A binary semaphore is
a semaphore with an integer value that can range only between 0 and 1. A binary semaphore can
be simpler to implement than a counting semaphore, depending on the underlying hardware
architecture. We will now show how a counting semaphore can be implemented using binary
semaphores. Let S be a counting semaphore.
To implement it in terms of binary semaphores we need the following data structures:
binary-semaphore S1, S2;
int C;
14
CS4TH3 OPERATING SYSTEMS
Initially S1 = 1, S2 = 0, and the value of integer C is set to the initial value of the counting
semaphore S.The wait operation on the counting semaphore S can be implemented as
follows:
wait (S1) ;
C--;
i f (C < 0) {
signal(S
1) ; wait
(S2) ;
}
signal(S1);
The signal operation on the counting semaphore S can be
implemented as follows: w a i t (S1) ;
C++ ;
i f (C <= 0)
signal (S2)
;e l s e
signal (S1)
;
Classic Problems of Synchronization
We present a number of different synchronization problems as examples for a large class of
concurrency-control problems. These problems are used for testing nearly every newly proposed
synchronization scheme. Semaphores are used for synchronization in our solutions.
signal (full);
}while (TRUE);
do {
wait (full);
wait(mutex);
...
//remove an item from buffer to nextc
...
signal(mutex);
signal(empty);
//consume the item in nextc
...
}while (TRUE);
16
CS4TH3 OPERATING SYSTEMS
variable keeps track of how many processes are currently reading the object. The
semaphore wrt functions as a mutual-exclusion semaphore for the writers.
The code for a writer process is shown:
Consider five philosophers who spend their lives thinking and eating. The
philosophers share a circular table surrounded by five chairs, each belonging to one
philosopher. In the center of the table is a bowl of rice, and the table is laid with five single
chopsticks.
When a philosopher thinks, she does not interact with her colleagues. From time to
time, a philosopher gets hungry and tries to pick up the two chopsticks that are closest to
her (the chopsticks that are between her and her left and right neighbors).
17
CS4TH3 OPERATING SYSTEMS
A philosopher may pick up only one chopstick at a time. Obviously, she cannot
pick up a chopstick that is already in the hand of a neighbor. When a hungry philosopher
has both her chopsticks at the same time, she eats without releasing her chopsticks. When
she is finished eating, she puts down both of her chopsticks and starts thinking again.
where all the elements of chopstick are initialized to 1. The structure of philosopher i is
shown
18
CS4TH3 OPERATING SYSTEMS
MONITORS
wait(mutex);
...
critical section
...
wait(mutex);
in this case, a deadlock will occur.
• suppose that a process omits the wait(mutex), or the signal(mutex), or both. in this case,
either mutual exclusion is violated or a deadlock will occur.
A type, or abstract data type, encapsulates private data with public methods to
operate on that data. A monitor type presents a set of programmer-defined operations that
are provided mutual exclusion within the monitor.
Monitor type also contains the declaration of variables whose values define the state
of an instance of that type, along with the bodies of procedures or functions that operate on
those variables. The syntax of a monitor is shown:
19
CS4TH3 OPERATING SYSTEMS
20
CS4TH3 OPERATING SYSTEMS
This solution imposes the restriction that a philosopher may pick up her chopsticks
only if both of them are available. To code this solution, we need to distinguish among
three states in which we may find a philosopher. For this purpose, we introduce the
following data structure:
21
CS4TH3 OPERATING SYSTEMS
Philosopher i can set the variable state [i] = eating only if her two neighbors are
not eating: (state [(i+4) % 5] != eating) and (state [(i+1) % 5] != eating). We also need
to declare
where philosopher i can delay herself when she is hungry but is unable to obtain
the chopsticks she needs.
The distribution of the chopsticks is controlled by the monitor dp, whose definition
is shown below.(next page)
Each philosopher, before starting to eat, must invoke the operation pickup (). This
may result in the suspension of the philosopher process. After the successful completion of
the operation, the philosopher may eat. Following this, the philosopher invokes the
putdown() operation. Thus, philosopher i must invoke the operations pickup() and
putdown() in the following sequence:
dp.pickup(i);
...
eat
...
dp.putdown(i);
22
CS4TH3 OPERATING SYSTEMS
23
CS4TH3 OPERATING SYSTEMS
wait(mutex) ;
...
body of F
...
if (next_count > 0)
signal(next);
else
signal(mutex);
if (x_count > 0) {
next_count++;
signal(x_sem);
wait(next) ;
next_count—;
}
24