0% found this document useful (0 votes)
23 views

Lecture07 Sync

This document discusses synchronization principles for operating systems including the critical section problem, solutions like semaphores and mutex locks, and issues like race conditions. It provides examples of the bounded buffer problem and how to ensure atomic operations on shared counters to prevent race conditions. It also covers eliminating concurrency to avoid context switches in critical sections and why simply disabling interrupts is not a full solution.

Uploaded by

Amanuel mergia
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)
23 views

Lecture07 Sync

This document discusses synchronization principles for operating systems including the critical section problem, solutions like semaphores and mutex locks, and issues like race conditions. It provides examples of the bounded buffer problem and how to ensure atomic operations on shared counters to prevent race conditions. It also covers eliminating concurrency to avoid context switches in critical sections and why simply disabling interrupts is not a full solution.

Uploaded by

Amanuel mergia
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/ 9

Operating Systems 9/20/2018

Synchronization Principles
• Background
– Concurrent access to shared data may result in data
inconsistency.
– Maintaining data consistency requires mechanisms
to ensure the orderly execution of cooperating
Synchronization Principles processes.
• The Critical-Section Problem
– Pure software solution
CS 256/456 – With help from the hardware
Dept. of Computer Science, University • Synchronization without busy waiting (with the support of
of Rochester process/thread scheduler)
– Semaphore
– Mutex lock
– Condition variables
9/20/2018 CSC 2/456 1 9/20/2018 CSC 2/456 2

Bounded Buffer Bounded Buffer


• Shared data counter • Shared data counter

typedef struct { ... } item; typedef struct { ... } item;


out in out in
item buffer[BUFFER_SIZE]; item buffer[BUFFER_SIZE];
int in = 0, out = 0; int in = 0, out = 0;
int counter = 0; int counter = 0;

• Producer process • Consumer process • Producer process • Consumer process


item nextProduced; item nextConsumed; item nextProduced; item nextConsumed;
while (1) { while (1) { while (1) { while (1) {
while (counter==BUFFER_SIZE) while (counter==0) while (in==out)
while (((in+1)%BUFFER_SIZE==out)
; /* do nothing */ ; /* do nothing */
; /* do nothing */ ; /* do nothing */
nextConsumed = buffer[out]; nextConsumed = buffer[out];
buffer[in] = nextProduced; buffer[in] = nextProduced;
out = (out+1) % BUFFER_SIZE; out = (out+1) % BUFFER_SIZE;
in = (in+1) % BUFFER_SIZE; counter--; in = (in+1) % BUFFER_SIZE; counter--;
counter++; } counter++; }
} }

9/20/2018 CSC 2/456 3 9/20/2018 CSC 2/456 4

CSC 256/456 1
Operating Systems 9/20/2018

Bounded Buffer Race Condition


• The following statements must be performed atomically:
counter++;
counter--; • Race condition:
• Atomic operation means an operation that completes in its entirety – The situation where several processes access and
without interruption. manipulate shared data concurrently.
– The final value of the shared data and/or effects on
• The statement “counter++” may be compiled into the following the participating processes depends upon the order of
instruction sequence: process execution – nondeterminism.
register1 = counter;
register1 = register1 + 1;
• To prevent race conditions, concurrent processes must be
counter = register1;
synchronized.
• The statement “counter--” may be compiled into:
register2 = counter;
register2 = register2 - 1;
counter = register2;

9/20/2018 CSC 2/456 5 9/20/2018 CSC 2/456 6

The Critical-Section Problem Eliminating Concurrency


• Problem context: • First idea: eliminating the chance of context switch when a
process runs in the critical section.
– n processes all competing to use some shared – effective as a complete solution only on a single-
data processor machine
– Each process has a code segment, called critical – only for short critical sections
section, in which the shared data is accessed.
• Find a solution that satisfies the following: • How to eliminate context switch?
1. Mutual Exclusion. No two processes simultaneously in the critical – software exceptions
section. – hardware interrupts
2. Progress. No process running outside its critical section may
block other processes. – system calls
3. Bounded Waiting/Fairness. Given the set of concurrent • Disabling interrupts?
processes, a bound must exist on the number of times that other – not feasible for user programs since they shouldn’t
processes are allowed to enter their critical sections after a
process has made a request to enter its critical section and be able to disable interrupts
before that request is granted. – feasible for OS kernel programs
9/20/2018 CSC 2/456 7 9/20/2018 CSC 2/456 8

CSC 256/456 2
Operating Systems 9/20/2018

Critical Section for Two Processes Algorithm 1


• Shared variables:
• Only 2 processes, P0 and P1 – int turn;
• General structure of process Pi (other process Pj) initially turn = 0;
do {
– turn==i  Pi can enter its critical section
entry section
critical section • Process Pi
exit section do {
remainder section while (turn != i) ;
} while (1); critical section
• Processes may share some common variables to synchronize their turn = j;
actions. remainder section
} while (1);
• Assumption: instructions are atomic and no re-ordering of
instructions. • Satisfies mutual exclusion, but not progress

9/20/2018 CSC 2/456 9 9/20/2018 CSC 2/456 10

Algorithm 2 Algorithm 3
• Shared variables:
• Combine shared variables of algorithms 1 and 2.
– boolean flag[2];
initially flag[0] = flag[1] = false; • Process Pi
– flag[i]==true  Pi ready to enter its critical do {
section flag[i] = true;
turn = j;
• Process Pi while (flag[j] && turn==j) ;
do { critical section
flag[i] = true; flag[i] = false;
while (flag[j]) ; remainder section
critical section } while (1);
flag[i] = false;
remainder section • Meets all three requirements; solves the critical-section
} while (1); problem for two processes.  called Peterson’s algorithm.

• Satisfies mutual exclusion, but not progress requirement.

9/20/2018 CSC 2/456 11 9/20/2018 CSC 2/456 12

CSC 256/456 3
Operating Systems 9/20/2018

Basic Hardware Mechanisms for Synchronization Using Special


Synchronization Instruction: TSL (test-and-set)
• Test-and-set – atomic exchange entry_section:
TSL R1, LOCK | copy lock to R1 and set lock to 1
• Fetch-and-op (e.g., increment) – returns value and CMP R1, #0 | was lock zero?
atomically performs op (e.g., increments it) JNE entry_section | if it wasn’t zero, lock was set, so loop
RET | return; critical section entered
• Compare-and-swap – compares the contents of two
locations and swaps if identical exit_section:
MOV LOCK, #0 | store 0 into lock
• Load-locked/store conditional – pair of instructions – RET | return; out of critical section
deduce atomicity if second instruction returns correct
value
• Does it solve the synchronization problem?
• Does it work for multiple (>2) processes?

9/20/2018 CSC 2/456 13 9/20/2018 CSC 2/456 14

Implementing Locks Using


Using ll/sc for Atomic Exchange
Test&Set
• On the SPARC ldstub moves an unsigned byte into the
destination register and rewrites the same byte in • Swap the contents of R4 with the memory location
memory to all 1s specified by R1
_Lock_acquire:
ldstub [%o0], %o1 try: mov R3, R4 ; mov exchange value
addcc %g0, %o1, %g0
bne _Lock
ll R2, 0(R1) ; load linked
nop sc R3, 0(R1) ; store conditional
fin: beqz R3, try ; branch if store fails
jmpl %r15+8, %g0
mov R4, R2 ; put load value in R4
nop
_Lock_release:
st %g0, [%o0]
jmpl %r15+8, %g0
9/20/2018nop CSC 2/456 15 9/20/2018 CSC 2/456 16

CSC 256/456 4
Operating Systems 9/20/2018

Busy Wait vs. Scheduler-Based


Semaphore
Synchronization
• In all our solutions so far, a process enters a loop until the • Synchronization tool that does not • Solving the critical section
entry is granted  busy waiting. require busy waiting. problem:

• Semaphore S – integer variable Shared data:


• Problems with busy waiting: which can only be accessed via two semaphore mutex=1;
– Waste of CPU time atomic operations
Process Pi:
– If a process is switched out of CPU during critical • Semantics (roughly) of the two wait(mutex);
section operations: critical section
• other processes may have to waste a whole CPU wait(S) or P(S):
signal(mutex);
quantum remainder section
wait until S>0;
• may even deadlock with strictly prioritized S--;
scheduling (priority inversion problem)
signal(S) or V(S):
• Solution
S++;
– Avoid busy wait as much as possible (yield the
processor instead) – scheduler-based synchronization
– If you can’t avoid busy wait, you must prevent
context switch during critical section (disable
9/20/2018interrupts while in the kernel)
CSC 2/456 17 9/20/2018 CSC 2/456 18

Semaphore Implementation Mutex Lock (Binary Semaphore)


• Semaphore operations now defined as
• Define a semaphore as a (both are atomic): • Mutex lock – a semaphore with only two state: locked/unlocked
record wait(S):
typedef struct { • Semantics of the two (atomic) operations:
value = (S.value--);
int value; if (value < 0) { lock(mutex):
proc_list *L;
add this process to S.L; wait until mutex==unlocked;
} semaphore; mutex=locked;
block;
• Assume two simple }
unlock(mutex):
operations: signal(S):
value = (S.value++); mutex=unlocked;
– block suspends the
process that invokes it. if (value <= 0) {
remove a process P from S.L; • Can you implement mutex lock using semaphore?
– wakeup(P) resumes the
wakeup(P);
execution of a blocked
process P.
} • How about the opposite?

How do we make sure wait(S) and signal(S) are atomic?


So have we truly removed busy waiting?

9/20/2018 CSC 2/456 19 9/20/2018 CSC 2/456 20

CSC 256/456 5
Operating Systems 9/20/2018

Implement Semaphore Using Mutex Classical Problems of


Lock Synchronization
• wait operation:
lock(L1);
• Data structures: C --; • Bounded-Buffer Problem
mutex_lock L1, L2; if (C < 0) {
int C; unlock(L1);
lock(L2); • Dining-Philosophers Problem
• Initialization: }
L1 = unlocked; unlock(L1);
L2 = locked;
• signal operation:
C = initial value of semaphore;
lock(L1);
C ++;
if (C <= 0)
unlock(L2);
else
unlock(L1);

9/20/2018 CSC 2/456 21 9/20/2018 CSC 2/456 22

Bounded Buffer Bounded Buffer Problem


counter • Shared data
• Shared data
buffer;
typedef struct { ... } item;
out in
item buffer[BUFFER_SIZE];
int in = 0, out = 0;
int counter = 0; • Producer process • Consumer process
while (1) { while (1) {
...
• Producer process • Consumer process ...
remove an item from buffer to nextc;
produce an item in nextp;
item nextProduced; item nextConsumed; ...
...
while (1) { while (1) { consume nextc;
add nextp to buffer; ...
while (counter==BUFFER_SIZE) while (counter==0)
; /* do nothing */ ... }
; /* do nothing */ }
nextConsumed = buffer[out];
buffer[in] = nextProduced;
out = (out+1) % BUFFER_SIZE;
in = (in+1) % BUFFER_SIZE; counter--;
counter++; }
} • Protecting the critical section for safe concurrent execution.
• Synchronizing producer and consumer when buffer is empty/full.
9/20/2018 CSC 2/456 23 9/20/2018 CSC 2/456 24

CSC 256/456 6
Operating Systems 9/20/2018

Bounded Buffer Solution


• Shared data
Dining-Philosophers Problem
buffer;
semaphore full=0;
semaphore empty=n; • Philosopher i (1 ≤ i ≤ 5):
semaphore mutex=1;
while (1) {
...
• Producer process • Consumer process eat;
while (1) { while (1) { ...
... ... think;
wait(full); ...
produce an item in nextp;
wait(mutex); }
...
remove an item from buffer to nextc;
wait(empty);
signal(mutex);
wait(mutex); signal(empty);
add nextp to buffer; ...
signal(mutex); consume nextc;
signal(full); ... • eating needs both chopsticks (the left and the right one).
... }
}
9/20/2018 CSC 2/456 25 9/20/2018 CSC 2/456 26

Dining-Philosophers: Possible Solution



Monitors
High-level synchronization construct that allows the safe sharing
• Shared data:
of an abstract data type among concurrent processes.
semaphore chopstick[5]; • Native support for mutual exclusion.
Initially all values are 1;
monitor monitor-name
{
• Philosopher i: shared variable declarations
procedure body P1 (...) {
while(1) {
. . .
...
}
wait(chopstick[i]);
procedure body Pn (...) {
wait(chopstick[(i+1) % 5]);
. . .
eat;
}
signal(chopstick[i]);
{
signal(chopstick[(i+1) % 5]);
initialization code
... Deadlock? }
think;
}
...
};

9/20/2018 CSC 2/456 27 9/20/2018 CSC 2/456 28

CSC 256/456 7
Operating Systems 9/20/2018

Two Semantics of Condition


Condition Variables in Monitors Variables
• To allow a process to wait within the monitor, a • Hoare semantics:
condition variable must be declared, as – p0 executes signal while p1 is waiting  p0 immediately yields the
condition x, y; monitor to p1
– The logical condition holds when P1 gets to run
• Condition variable can only be used with the operations
wait and signal. if (resourceNotAvailable()) Condition.wait();
– The operation /* now available ... continue ... */
x.wait(); . . .
means that the process invoking this operation is
suspended until another process invokes • Alternative semantics:
x.signal(); – p0 executes signal while p1 is waiting  p0 continues to execute,
then when p0 exits the monitor p1 can receive the signal
– The x.signal operation resumes exactly one – The logical condition may not hold when P1 gets to run
suspended process. If no process is suspended, then
– Brinch Hansen (“Mesa”) semantics: p0 must exit the monitor after
the signal operation has no effect. a signal
• Unlike semaphore, there is no counting in condition
variables
9/20/2018 CSC 2/456 29 9/20/2018 CSC 2/456 30

Dining Philosophers Alternative


Dining Philosophers Solution
monitor dp { monitor dp { Solution
enum {THINKING, HUNGRY, EATING} state[5];
condition self[5]; enum {thinking, eating} state[5];
void pickup(int i) { condition cond[5];
state[i] = HUNGRY;
test(i); void pickup(int i) {
if (state[i] != EATING)
self[i].wait(); while (state[(i+4)%5]==eating || state[(i+1)%5]==eating)
} cond[i].wait();
void putdown (int i) { state[i] = eating;
state[i] = THINKING; }
test((i+4)%5);
test((i+1)%5);
}
void putdown(int i) {
state[i] = thinking;
void test (int i) {
if (state[(i+4)%5]!=EATING && state[(i+1)%5]!=EATING && state[i] == HUNGRY) {
cond[(i+4)%5].signal();
state[i] = EATING; cond[(i+1)%5].signal();
self[i].signal();
}
}
}
void init() {
void init() { for (int i=0; i<5; i++)
for (int i=0; i<5; i++) state[i] = thinking;
state[i] = THINKING;
} }
} }

9/20/2018 CSC 2/456 31 9/20/2018 CSC 2/456 32

CSC 256/456 8
Operating Systems 9/20/2018

Disclaimer
• Parts of the lecture slides contain original work from Gary
Nutt, Andrew S. Tanenbaum, and Kai Shen. The slides are
intended for the sole purpose of instruction of operating
systems at the University of Rochester. All copyrighted
materials belong to their original owner(s).

9/20/2018 CSC 2/456 111

CSC 256/456 9

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy