[go: up one dir, main page]

0% found this document useful (0 votes)
7 views83 pages

Chapter02 4

The document discusses synchronization in operating systems, focusing on cooperating processes and various synchronization hardware solutions such as locks, semaphores, and atomic instructions. It covers classical concurrency problems like the Producer/Consumer problem, Readers and Writers, and the Dining Philosophers, detailing how semaphores can be used to manage access to shared resources. Additionally, it highlights the challenges of implementing these synchronization mechanisms, including issues like deadlock and starvation.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views83 pages

Chapter02 4

The document discusses synchronization in operating systems, focusing on cooperating processes and various synchronization hardware solutions such as locks, semaphores, and atomic instructions. It covers classical concurrency problems like the Producer/Consumer problem, Readers and Writers, and the Dining Philosophers, detailing how semaphores can be used to manage access to shared resources. Additionally, it highlights the challenges of implementing these synchronization mechanisms, including issues like deadlock and starvation.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 83

Operating Systems

Synchronization
Solutions
Cooperating Processes

• Introduction to Cooperating Processes


• Producer/Consumer Problem
• The Critical-Section Problem
• Synchronization Hardware
• Semaphores

2
Synchronization Hardware
• Many systems provide hardware support for
implementing the critical section code.
• All solutions below based on idea of locking:
– Protecting critical regions via locks.
• Uniprocessors – could disable interrupts:
– Currently running code would execute without preemption.
– Generally too inefficient on multiprocessor systems:
• Operating systems using this are not broadly scalable.
• Modern machines provide special atomic (non-
interruptible) hardware instructions:
– Either test memory word and set value at once.
– Or swap contents of two memory words.
3
Interrupt Disabling
• On a Uniprocessor:
mutual exclusion is preserved
but efficiency of execution is
degraded: while in CS, we Process Pi:
cannot interleave execution repeat
with other processes that disable interrupts
are in RS. critical section
• On a Multiprocessor: enable interrupts
mutual exclusion is not remainder section
preserved: forever
– CS is now atomic but not
mutually exclusive
(interrupts are not disabled
on other processors).
4
Special Machine Instructions
• Normally, access to a memory location
excludes other access to that same location.
• Extension: designers have proposed machines
instructions that perform 2 actions atomically
(indivisible) on the same memory location
(e.g., reading and writing).
• The execution of such an instruction is also
mutually exclusive (even on Multiprocessors).

5
Solution to Critical Section Problem using Locks

• The general layout is of lock solution is:

do {
acquire lock
critical section
release lock
remainder section
} while (TRUE);
6
TestAndSet Synchronization Hardware
• Test and set (modify) the content of a word atomically (a
Boolean version):
boolean TestAndSet(boolean *target) {
boolean rv = *target;
*target = TRUE;
return rv;
}
1. Executed atomically.
2. Returns the original value of passed parameter.
3. Set the new value of passed parameter to “TRUE”.
• The Boolean function represents the essence of the
corresponding machine instruction.
7
Mutual Exclusion with TestAndSet
• Shared data:
boolean lock = FALSE;
Process Pi
do {
while (TestAndSet(&lock));
critical section
lock = FALSE;
remainder section
} while (TRUE);

8
Another Test-and-Set Example
• An algorithm that uses testset for
• A C++ description Mutual Exclusion:
of machine instruction – Shared variable b is initialized to 0
of test-and-set:
– Only first Pi who sets b enters CS
bool testset(int& i) Process Pi:
{ repeat
if (i==0) { repeat{}
i=1; until testset(b);
return TRUE; CS
} else { b:=0;
return FALSE; RS
} forever
9 }
Test-and-Set Instruction at Assembly Level

10 A. Frank - P. Weisberg
Swap Synchronization Hardware
• Atomically swap (exchange) two variables:
void Swap(boolean *a, boolean *b) {
boolean temp = *a;
*a = *b;
*b = temp;
}
• The procedure represents the essence of the
corresponding machine instruction.
11
Mutual Exclusion with Swap
• Shared data:
boolean lock = FALSE;
• Process Pi
do {
/* Each process has a local Boolean variable key */
key = TRUE;
while (key == TRUE)
Swap(&lock, &key);
critical section
lock = FALSE;
remainder section
} while (TRUE);
12
Swap instruction at Assembly Level

13 A. Frank - P. Weisberg
Compare and Swap Synchronization Hardware
int compare_and_swap(int *value, int expected, int
new_value) {
int temp = *value;

if (*value == expected)
*value = new_value;
return temp;
}
1. Executed atomically.
2. Returns the original value of passed parameter “value”.
3. Set the variable “value” the value of the passed parameter
“new_value” but only if “value” ==“expected”. That is, the
swap takes place only under this condition.
14
Mutual Exclusion with Compare and Swap
• Shared integer “lock” initialized to 0;
• Solution:
do {
while (compare_and_swap(&lock, 0, 1) != 0)
; /* do nothing */
/* critical section */
lock = 0;
/* remainder section */
} while (true);

15
Disadvantages of Special Machine Instructions

• Busy-waiting is employed, thus while a process


is waiting for access to a critical section it continues
to consume processor time.
• No Progress (Starvation) is possible when a process
leaves CS and more than one process is waiting.
• They can be used to provide mutual exclusion but
need to be complemented by other mechanisms to
satisfy the bounded waiting requirement of the CS
problem.
• See next slide for an example.
16
Critical Section Solution with TestAndSet()
• Process Pi
do {
waiting[i] = TRUE;
key = TRUE;
while (waiting[i] && key)
key = TestAndSet(&lock);
waiting[i] = FALSE;
critical section
j = (i + 1) % n;
while ((j != i) && !waiting[j])
j = (j + 1) % n;
if (j == i) lock = FALSE;
else waiting[j] = FALSE;
remainder section
17 } while (TRUE);
Mutex Locks (Spin Lock)
• Previous solutions are complicated and generally
inaccessible to application programmers.
• OS designers build software tools to solve critical
section problem.
• Simplest is mutex lock.
• Protect a critical section by first acquire() a lock then
release() the lock:
– Boolean variable indicating if lock is available or not.
• Calls to acquire() and release() must be atomic:
– Usually implemented via hardware atomic instructions.
• But this solution requires busy waiting:
18 – This lock therefore called a spinlock.
acquire() and release()
• acquire() {
while (!available)
; /* busy wait */
available = false;;
}
• release() {
available = true;
}
• do {
acquire lock
critical section
release lock
remainder section
} while (true);
19
Semaphores (1)
• Synchronization tool that provides more
sophisticated ways (than Mutex locks)
for process to synchronize their activities.
• Logically, a semaphore S is an integer
variable that, apart from initialization, can
only be changed through 2 atomic and
mutually exclusive operations:
– wait(S) (also down(S))
– signal(S) (also up(S))
• Less complicated.
20
Critical Section of n Processes
• Shared data:
semaphore mutex; // initiallized to 1
• Process Pi:
do {
wait(mutex);
critical section
signal(mutex);
remainder section
} while (TRUE);
21
Semaphores (2)
• Access is via two atomic operations:

wait (S) :
while (S <= 0);
S--;

signal (S) :
S++;

22
Semaphores (3)
• Must guarantee that no 2 processes can execute wait()
and signal() on the same semaphore at the same time.
• Thus, implementation becomes the critical section
problem where the wait and signal code are placed in
the critical section.
– Could now have busy waiting in CS implementation:
• But implementation code is short
• Little busy waiting if critical section rarely occupied.
• Note that applications may spend lots of time in
critical sections and therefore this is not a good
solution.
23
Semaphores (4)
• To avoid busy waiting, when a process has to wait, it will be
put in a blocked queue of processes waiting for the same event.
• Hence, in fact, a semaphore is a record (structure):
type semaphore = record
count: integer;
queue: list of process
end;
var S: semaphore;
• With each semaphore there is an associated waiting queue.
• Each entry in a waiting queue has 2 data items:
– value (of type integer)
– pointer to next record in the list
24
Semaphore’s operations
• When a process must wait for a semaphore S, it is blocked and
put on the semaphore’s queue.
• Signal operation removes (assume a fair policy like FIFO) first
process from the queue and puts it on list of ready processes.
wait(S):
S.count--;
if (S.count<0) {
block this process
place this process in S.queue
}
signal(S):
S.count++;
if (S.count<=0) {
remove a process P from S.queue
place this process P on ready list
25 }
Semaphore Implementation (1)
• Define semaphore as a C struct:
typedef struct {
int value;
struct process *list;
} semaphore;
• Assume two operations:
– Block: place the process invoking the operation
on the appropriate waiting queue.
– Wakeup: remove one of processes in the waiting
queue and place it in the ready queue.
26
Semaphore Implementation (2)
• Semaphore operations now defined as
wait (semaphore *S) {
S->value--;
if (S->value < 0) {
add this process to S->list;
block();
}
}

signal (semaphore *S) {


S->value++;
if (S->value <= 0) {
remove a process P from S->list;
wakeup(P);
}
27 }
Two Types of Semaphores
1. Counting semaphore – integer value can range
over an unrestricted domain.
2. Binary semaphore – integer value can range
only between 0 and 1 (really a Boolean);
can be simpler to implement (use waitB and
signalB operations); same as Mutex lock.
• We can implement a counting semaphore S
using 2 binary semaphores (that protect its
counter).
28
Binary semaphores
waitB(S):
if (S.count = 1) {
S.count := 0;
} else {
block this process
place this process in S.queue
}

signalB(S):
if (S.queue is empty) {
S.count := 1;
} else {
remove a process P from S.queue
place this process P on ready list
}
29
Implementing S as a Binary Semaphore (1)
• Data structures:
binary-semaphore S1, S2;
int C;
• Initialization:
S1 = 1
S2 = 0
C = initial value of counting
semaphore S

30
Implementing S as a Binary Semaphore (2)
• wait operation:
waitb(S1);
C--;
if (C < 0) {
signalb(S1);
waitb(S2); }
signalb(S1);
• signal operation:
waitb(S1);
C++;
if (C <= 0)
signalb(S2);
else signalb(S1);
31
Semaphore as a General Synchronization Tool

• Execute B in Pj only after A executed in Pi


• Use semaphore flag initialized to 0.
• Code:
Pi Pj
M M
A wait(flag)
signal(flag) B
32
Deadlock and Starvation
• Deadlock – two or more processes are waiting indefinitely for
an event that can be caused by only one of waiting processes.
• Let S and Q be two semaphores initialized to 1
P0 P1
wait(S); wait(Q);
wait(Q); wait(S);
M M
signal(S); signal(Q);
signal(Q) signal(S);
• Starvation – indefinite blocking. A process may never be
removed from the semaphore queue (say, if LIFO) in which
it is suspended.
• Priority Inversion – scheduling problem when lower-priority
process holds a lock needed by higher-priority process
33
Problems with Semaphores
• Semaphores provide a powerful tool for enforcing
mutual exclusion and coordinate processes.
• But wait(S) and signal(S) are scattered among several
processes. Hence, difficult to understand their effects.
• Usage must be correct in all the processes (correct
order, correct variables, no omissions).
• Incorrect use of semaphore operations:
– signal (mutex) …. wait (mutex)
– wait (mutex) … wait (mutex)
– Omitting of wait (mutex) or signal (mutex) (or both)
• One bad (or malicious) process can fail the entire
collection of processes.
34
Operating Systems

Classical Problems
of Concurrency
Introduction to Concurrency

• Classical Problems of Concurrency


• Monitors
• Inter-Process Communication (IPC)
• Synchronization Examples

36
Classical Problems of Concurrency
• There are many of them –
let’s briefly see three
famous problems:
1. P/C Bounded-Buffer
2. Readers and Writers
3. Dining-Philosophers

37
Reminder: P/C problem with race condition

38 A. Frank - P. Weisberg
P/C Bounded-Buffer Problem
• We need 3 semaphores:
1. A semaphore mutex (initialized to 1) to
have mutual exclusion on buffer access.
2. A semaphore full (initialized to 0) to
synchronize producer and consumer on
the number of consumable items.
3. A semaphore empty (initialized to n) to
synchronize producer and consumer on
the number of empty spaces.
39
Bounded-Buffer – Semaphores
• Shared data

semaphore full, empty, mutex;

Initially:

full = 0, empty = n, mutex = 1

40
Bounded-Buffer – Producer Process
do {

produce an item in nextp

wait(empty);
wait(mutex);

add nextp to buffer

signal(mutex);
signal(full);
} while (TRUE);
41
Bounded-Buffer – Consumer Process
do {
wait(full)
wait(mutex);

remove an item from buffer to nextc

signal(mutex);
signal(empty);

consume the item in nextc

} while (TRUE);
42
Notes on P/C Bounded-Buffer Solution

• Remarks (from consumer point of view):


– Putting signal(empty) inside the CS of the
consumer (instead of outside) has no effect since
the producer must always wait for both semaphores
before proceeding.
– The consumer must perform wait(full) before
wait(mutex), otherwise deadlock occurs if
consumer enters CS while the buffer is empty.
• Conclusion: using semaphores is a
difficult art ... ☺
43
Full P/C Bounded-Buffer Solution

44 A. Frank - P. Weisberg
Readers-Writers Problem
• A data set/repository is shared among a number
of concurrent processes:
– Readers – only read the data set; they do not
perform any updates.
– Writers – can both read and write.
• Problem – allow multiple readers to read at the
same time. Only one single writer can access
the shared data at the same time.

45
Readers-Writers Dynamics
• Any number of reader activities and writer
activities are running.
• At any time, a reader activity may wish to read
data.
• At any time, a writer activity may want to
modify the data.
• Any number of readers may access the data
simultaneously.
• During the time a writer is writing, no other
reader or writer may access the shared data.
46
Readers-Writers with active readers

47 A. Frank - P. Weisberg
Readers-Writers with an active writer

48 A. Frank - P. Weisberg
Should readers wait for waiting writer?

49 A. Frank - P. Weisberg
Readers-Writers problem
• There are various versions with different
readers and writers preferences:
1. The first readers-writers problem, requires that no
reader will be kept waiting unless a writer has
obtained access to the shared data.
2. The second readers-writers problem, requires that
once a writer is ready, no new readers may start
reading.
3. In a solution to the first case writers may starve;
In a solution to the second case readers may starve.

50
First Readers-Writers Solution (1)
• readcount (initialized to 0) counter keeps
track of how many processes are currently
reading.
• mutex semaphore (initialized to 1) provides
mutual exclusion for updating readcount.
• wrt semaphore (initialized to 1) provides mutual
exclusion for the writers; it is also used by the
first or last reader that enters or exits the CS.

51
First Readers-Writers Solution (2)

• Shared data
semaphore mutex, wrt;
int readcount;
Initially
mutex = 1, wrt = 1, readcount = 0

52
First Readers-Writers – Writer Process

do {
wait(wrt);

writing is performed

signal(wrt);
} while(TRUE);
53
First Readers-Writers – Reader Process
do {
wait(mutex);
readcount++;
if (readcount == 1)
wait(wrt);
signal(mutex);

reading is performed

wait(mutex);
readcount--;
if (readcount == 0)
signal(wrt);
signal(mutex);
} while(TRUE);
54
Dining Philosophers Problem (1)
• Philosophers spend their lives
alternating between thinking and
eating.
• Five philosophers can be seated
around a circular table.
• There is a shared bowl of rice.
• In front of each one is a plate.
• Between each pair of philosophers
there is a chopstick, so there are
five chopsticks.
• It takes two chopsticks to take/eat
rice, so while n is eating neither
n+1 nor n-1 can be eating.
55
Dining Philosophers Problem (2)
• Each one thinks for a while, gets the
chopsticks/forks needed, eats, and puts the
chopsticks/forks down again, in an endless
cycle.
• Illustrates the difficulty of allocating resources
among process without deadlock and starvation.

56
Dining Philosophers Problem (3)
• The challenge is to grant requests for chopsticks
while avoiding deadlock and starvation.
• Deadlock can occur if everyone tries to get their
chopsticks at once. Each gets a left chopstick,
and is stuck, because each right chopstick is
someone else’s left chopstick.

57
Dining Philosophers Solution (1)
• Each philosopher
is a process. Process Pi:
repeat
• One semaphore think;
per fork: wait(fork[i]);
wait(fork[i+1 mod 5]);
– fork: array[0..4] eat;
of semaphores signal(fork[i+1 mod 5]);
– Initialization: signal(fork[i]);
fork[i].count := 1 forever
for i := 0..4
Note: deadlock if each philosopher starts by picking up
his left fork!
58
Dining Philosophers Solution (2)
Possible solutions to avoid deadlock:
• Allow at most four philosophers to be sitting
simultaneously at the table.
• Allow a philosopher to pick up the forks only
if both are available (picking must be done in
a critical section).
• Use an asymmetric solution - an odd-numbered
philosopher picks up first the left chopstick
and then the right chopstick. Even-numbered
philosopher picks up first the right chopstick
and then the left chopstick.
59
Dining Philosophers Solution (4)
• A solution: admit only 4
philosophers at a time Process Pi:
that try to eat. repeat
• Then 1 philosopher can think;
always eat when the other wait(T);
3 are holding 1 fork. wait(fork[i]);
wait(fork[i+1 mod 5]);
• Hence, we can use another eat;
semaphore T that would signal(fork[i+1 mod 5]);
limit at 4 the number of signal(fork[i]);
philosophers “sitting at signal(T);
the table”. forever
• Initialize: T.count := 4
60
Dining Philosophers Solution (5)

...

61 A. Frank - P. Weisberg
Dining Philosophers Problem (6)

62 A. Frank - P. Weisberg
Monitors
• The monitor is a programming-language
construct that provides equivalent functionality
to that of semaphores and that is easier to
control.
• Implemented in a number of programming
languages, including
– Concurrent Pascal, Pascal-Plus,
– Modula-2, Modula-3, and Java.

63
Monitors Characteristics
• Local data variables are accessible only by the
monitor
• Process enters monitor by invoking one of its
procedures
• Only one process may be executing in the
monitor at a time

64
Synchronization
• Synchronisation achieved by condition
variables within a monitor
– only accessible by the monitor.
• Monitor Functions:
– Cwait(c): Suspend execution of the calling process
on condition c
– Csignal(c) Resume execution of some process
blocked after a cwait on the same condition

65
Structure of a Monitor

66
Monitor With Condition Variables

67 A. Frank - P. Weisberg
Solution using Monitors

68
Monitor solution cont.

69
Inter-Process Communication (IPC)
• Mechanism for processes to communicate and
to synchronize their actions.
• Message system – processes communicate with
each other without resorting to shared variables.
• We have at least two primitives:
– send(destination, message) or send(message)
– receive(source, message) or receive(message)
• Message size is fixed or variable.

70
Basic Message-passing Primitives

71 A. Frank - P. Weisberg
Message format
• Consists of header and
body of message.
• In Unix: no ID, only
message type.
• Control info:
– what to do if run out of
buffer space.
– sequence numbers.
– priority.
• Queuing discipline: usually
FIFO but can also include
priorities.
72
Mutual Exclusion – Message Passing
• create a mailbox mutex Process Pi:
shared by n processes. var msg: message;
• send() is non-blocking. repeat
receive(mutex,msg);
• receive() blocks when CS
mutex is empty. send(mutex,msg);
• Initialization: RS
send(mutex, “go”); forever
• The first Pi who executes
receive() will enter CS.
Others will be blocked
until Pi resends msg.
73
Bounded-Buffer – Message Passing
• The producer place items (inside messages) in the
mailbox mayconsume.
• mayconsume acts as our buffer: consumer can
consume item when at least one message present.
• Mailbox mayproduce is filled initially with k null
messages (k= buffer size).
• The size of mayproduce shrinks with each
production and grows with each consumption.
• Solution can support multiple producers/consumers.
74
Bounded-Buffer – Message Passing
Producer:
var pmsg: message;
repeat
receive(mayproduce, pmsg);
pmsg := produce();
send(mayconsume, pmsg);
forever

Consumer:
var cmsg: message;
repeat
receive(mayconsume, cmsg);
consume(cmsg);
send(mayproduce, null);
forever
75
P/C Problem with Message Passing (1)

76
P/C Problem with Message Passing (2)

77
Resource starvation
• Starvation is similar to deadlock in that it causes a
process to freeze. Two or more processes become
deadlocked when each of them is doing nothing while
waiting for a resource occupied by another one.
• On the other hand, a process is in starvation when it is
waiting for a resource that simply keeps getting given
to other processes.
• A possible solution to starvation is to use a scheduling
algorithm with priority queue that increasing the
priority of processes that wait in the system for a long
time.
78
Synchronization Examples

• Solaris
• Windows XP
• Linux
• Pthreads

79
Solaris Synchronization
• Implements a variety of locks to support multitasking, multithreading
(including real-time threads), and multiprocessing.
• Uses adaptive mutexes for efficiency when protecting data from short code
segments:
– Starts as a standard semaphore spin-lock.
– If lock held, and by a thread running on another CPU, spins.
– If lock held by non-run-state thread, block and sleep waiting for signal of lock being
released.
• Uses condition variables.
• Uses readers-writers locks when longer sections of code need access to data.
• Uses turnstiles to order the list of threads waiting to acquire either an
adaptive mutex or reader-writer lock:
– Turnstiles are per-lock-holding-thread, not per-object.
• Priority-inheritance per-turnstile gives the running thread the highest of the
priorities of the threads in its turnstile.
80
Windows XP Synchronization
• Uses interrupt masks to protect access to global
resources on uniprocessor systems.
• Uses spinlocks on multiprocessor systems:
– Spinlocking-thread will never be preempted.
• Also provides dispatcher objects user-land which may
act mutexes, semaphores, events, and timers:
– Events
• An event acts much like a condition variable.
– Timers notify one or more thread when time expired.
– Dispatcher objects either signaled-state (object available) or
non-signaled state (thread will block).
81
Linux Synchronization
• Linux:
– Prior to kernel Version 2.6, disables interrupts to implement
short critical sections.
– Version 2.6 and later, fully preemptive.
• Linux provides:
– Semaphores
– Atomic integers
– spinlocks
– reader-writer versions of both.
• On single-CPU system, spinlocks replaced by enabling
and disabling kernel preemption.
82
Pthreads Synchronization

• Pthreads API is OS-independent.


• It provides:
– mutex locks
– condition variables
• Non-portable extensions include:
– read-write locks
– spinlocks

83

You might also like