Principles of Operating Systems: Lecture 4 - Process Synchronization Ardalan Amiri Sani
Principles of Operating Systems: Lecture 4 - Process Synchronization Ardalan Amiri Sani
Principles of Operating Systems: Lecture 4 - Process Synchronization Ardalan Amiri Sani
Operating Systems
Lecture 4 - Process Synchronization
Ardalan Amiri Sani ([email protected])
[lecture slides contains some content adapted from previous slides by Prof. Nalini Venkatasubramanian, and
course text slides © Silberschatz]
Outline
2
Producer-Consumer Problem
3
Bounded Buffer - message passing
message next_produced;
while (true) {
/* produce an item in next produced */ Producer
send(next_produced);
}
4
Bounded Buffer - Shared Memory
Solution
● Shared data
#define BUFFER_SIZE 10
typedef struct {
. . .
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
5
Bounded Buffer - Shared Memory
Solution: producer
item next_produced;
while (true) {
/* produce an item in next produced */
while (((in + 1) % BUFFER_SIZE) == out)
; /* do nothing */
buffer[in] = next_produced;
in = (in + 1) % BUFFER_SIZE;
}
6
Bounded Buffer - Shared Memory
Solution: consumer
item next_consumed;
while (true) {
while (in == out)
; /* do nothing */
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
7
Problem with this solution
8
Solution
● A solution that uses all N buffers is not that
simple.
● Modify producer-consumer code by adding a variable
counter, initialized to 0, incremented each time a new item is
added to the buffer
● Shared data
#define BUFFER_SIZE 10
typedef struct {
. . .
} item;
item buffer[BUFFER_SIZE];
int in = 0;
int out = 0;
int counter = 0; 9
Bounded Buffer - Shared Memory
Solution: producer
item next_produced;
while (true) {
/* produce an item in next produced */
10
Bounded Buffer - Shared Memory
Solution: consumer
item next_consumed;
while (true) {
while (counter == 0)
; /* do nothing */
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE;
counter--;
/* consume the item in next consumed */
}
11
Problem with this solution
12
Access to shared data
● The statements
counter++;
counter--;
must be executed atomically.
● Atomic operations
● An operation that runs to completion or not at all.
13
Race Condition
● Consider this execution interleaving with “count = 5” initially (we expect count = 5
in the end too):
S0: producer execute register1 = counter {register1 = 5}
S1: producer execute register1 = register1 + 1 {register1 = 6}
S2: consumer execute register2 = counter {register2 = 5}
S3: consumer execute register2 = register2 – 1 {register2 = 4}
S4: producer execute counter = register1 {counter = 6 }
S5: consumer execute counter = register2 {counter = 4 !!}
14
Race Condition
● If threads are working on separate data, we don’t need to worry about race
conditions:
Thread A Thread B
x = 1; y = 2;
● However, there can race conditions when we have shared data. Consider
the following (Initially, y = 12):
Thread A Thread B
x = 1; y = 2;
x = y+1; y = y*2;
● What are the possible values of x?
15
Race Condition
● If threads are working on separate data, we don’t need to worry about race
conditions:
Thread A Thread B
x = 1; y = 2;
● However, there can race conditions when we have shared data. Consider
the following (Initially, y = 12):
Thread A Thread B
x = 1; y = 2;
x = y+1; y = y*2;
● What are the possible values of x? Answer = (13, 3, 5)
● Or, what are the possible values of x below?
Thread A Thread B
x = 1; x = 2;
● What are the possible values of x?
16
Race Condition
● If threads are working on separate data, we don’t need to worry about race
conditions:
Thread A Thread B
x = 1; y = 2;
● However, there can race conditions when we have shared data. Consider
the following (Initially, y = 12):
Thread A Thread B
x = 1; y = 2;
x = y+1; y = y*2;
● What are the possible values of x? Answer = (13, 3, 5)
● Or, what are the possible values of x below?
Thread A Thread B
x = 1; x = 2;
● What are the possible values of x? Answer = (1, 2)
● X’s value is non-deterministic in the past two examples
17
Critical Section Problem
● Consider system of n processes {p0, p1, … pn-1} competing to
access shared data
● Each process has critical section segment of code
● Process may be changing common variables, updating
table, writing file, etc.
● When one process in critical section, no other may be in its
critical section
● Critical section problem is to design protocol to achieve/solve
this
● Each process must ask permission to enter critical section in
entry section, may follow critical section with exit section,
then remainder section
18
Critical Section
19
Critical Section Problem -
Requirements
● Mutual Exclusion
• If process Pi is executing in its critical section, then no other
processes can be executing in their critical sections.
● Progress
• If no process is executing in its critical section and there exists
some processes that wish to enter their critical section, then
the selection of the processes that will enter the critical section
next cannot be postponed indefinitely.
● Bounded Waiting
• A bound must exist on the number of times that other
processes are allowed to enter their critical sections after a
process has made a request to enter its critical section and
before that request is granted.
20
Critical Section Problem -
Assumptions
21
Solution: Critical Section Problem --
Initial Attempt
● Only 2 processes, Pi and Pj
● General structure of process Pi (Pj)
● Shared Variable:
● int turn = i;
● (turn == i) means that Pi can enter its critical section and (turn == j) means
that Pj can enter its critical section
● Process Pi Process Pj
do { do {
while (turn == j); while (turn == i);
critical section critical section
turn = j; turn = i;
remainder section remainder section
} while (true); } while (true);
23
Algorithm 1
● Satisfies mutual exclusion
● The turn is equal to either i or j and hence one of Pi and Pj
can enter the critical section
● Does not satisfy progress
● Example: Pi finishes the critical section and then gets stuck
indefinitely in its remainder section. Then Pj enters the
critical section, finishes, and then finishes its remainder
section. Pj then tries to enter the critical section again, but it
cannot since turn was set to i by Pj in the previous iteration.
Since Pi is stuck in the remainder section, turn will be equal
to i indefinitely and Pj can’t enter although it wants to. Hence
no process is in the critical section and hence no progress.
● We don’t need to discuss/consider bounded wait
when progress is not satisfied
24
Algorithm 2
● Shared Variables
● boolean flag[2];
flag[ i ] = false; flag[ j ] = false;
● (flag[ i ] == true) means that Pi ready to enter its critical section
● (flag[ j ] == true) means that Pj ready to enter its critical section
● Process Pi (Replace i with j and j with i for Pj)
do {
flag[i] = true;
while (flag[j]);
critical section
flag[i] = false;
remainder section
} while (true);
25
Algorithm 2
● Satisfies mutual exclusion
● If Pi enters, then flag[ i ] = true, and hence Pj will not enter.
● Does not satisfy progress
● Example: There can be an interleaving of execution in which
Pi and Pj both first set their flags to true and then both check
the other process’ flag. Therefore, both get stuck at the entry
section
● We don’t need to discuss/consider bounded wait
when progress is not satisfied
26
Algorithm 3
● Shared Variables
● boolean flag[2];
flag[ i ] = false; flag[ j ] = false;
● (flag[ i ] == true) means that Pi ready to enter its critical section
● (flag[ j ] == true) means that Pj ready to enter its critical section
● Process Pi (Replace i with j and j with i for Pj)
do {
while (flag[j]);
flag[i] = true;
critical section
flag[i] = false;
remainder section
} while (true);
27
Algorithm 3
● Does not satisfies mutual exclusion
● Example: There can be an interleaving of execution in which
both first check the other process’ flag and see that it is false.
Then they both enter the critical section.
● We don’t need to discuss/consider progress and
bounded wait when mutual exclusion is not satisfied
28
Algorithm 4
● Shared Variable:
● int turn = i;
● (turn == i) means that Pi can enter its critical section and (turn == j) means that Pj can
enter its critical section
● boolean flag[2];
flag[ i ] = false; flag[ j ] = false;
● (flag[ i ] == true) means that Pi ready to enter its critical section
● (flag[ j ] == true) means that Pj ready to enter its critical section
31
Bakery Algorithm
32
Bakery Algorithm (cont.)
● Notation -
● Lexicographic order(ticket#, process id#)
● (a,b) < (c,d) if (a<c) or if ((a=c) and (b < d))
● max(a0,….an-1) is a number, k, such that k >=ai
for i = 0,…,n-1
● Shared Data
boolean choosing[n]; (all items initialized to false)
int number[n]; (all initialized to 0)
33
Bakery Algorithm (cont.)
do {
choosing[i] = true;
number[i] := max(number[0], number[1],…,number[n-1]) + 1;
choosing[i] = false;
for (int j = 0; j < n, j++) {
while (choosing[j]);
while ((number[j] != 0) &&
((number[j], j) < (number[i],i));
}
critical section
number[i] = 0;
remainder section
} while (true);
34
Synchronization Hardware
● Software-based solutions such as Peterson’s solution and Bakery
algorithm are not guaranteed to work on modern computer
architectures due to how they perform basic machine-language
instructions (e.g., out-of-order execution)
● Many systems provide hardware support for implementing the
critical section.
● Solutions based on idea of locking
● Protecting the critical section via locks
35
Solution to Critical-section Problem Using Locks
do {
acquire lock
critical section
release lock
remainder section
} while (TRUE);
36
Synchronization Hardware
37
test_and_set Instruction
38
Solution using test_and_set()
39
Solution using test_and_set()
40
compare_and_swap Instruction
Definition:
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” to be the value of the passed parameter
“new_value” if “value” ==“expected”. That is, the swap takes place only
under this condition.
41
Solution using 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);
42
Bounded-waiting critical section implementation with
test_and_set
do {
waiting[i] = true;
key = true;
while (waiting[i] && key)
key = test_and_set(&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 */
} while (true);
43
Mutex Locks
● 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
● This lock therefore called a spinlock
44
acquire() and release()
●Semantics of
● acquire() {
while (!available)
; /* busy wait */
available = false;
}
●Semantics of
● release() {
available = true;
}
●Critical section implementation
do {
acquire lock
critical section
release lock
remainder section
} while (true);
45
Semaphore
● Semaphore S - integer variable (non-negative)
• used to represent number of abstract resources
● Can only be accessed via two atomic operations
wait (S): while (S <= 0);
S--;
signal (S): S++;
• P or wait used to acquire a resource, waits for semaphore to
become positive, then decrements it by 1
• V or signal releases a resource and increments the semaphore
by 1, waking up a waiting P, if any
• If P is performed on a count <= 0, process must wait for V or
the release of a resource.
P():“proberen” (to test) ; V() “verhogen” (to increment) in Dutch
46
Example: Critical Section for n
Processes
● Shared variables
semaphore mutex;
initially mutex = 1
● Process Pi
do {
wait(mutex);
critical section
signal(mutex);
remainder section
} while (true);
47
Semaphore as a General
Synchronization Tool
48
● Locks prevent conflicting actions on shared data
● Lock before entering critical section and before accessing shared data
Problem...
● Unlock when leaving, after accessing shared data
● Wait if locked
● Synchronization involves waiting
● Busy Waiting, uses CPU that others could use. This type of lock is
called a spinlock.
● Waiting thread may take cycles away from thread holding lock (no one
wins!)
● OK for short times since it prevents a context switch.
● Priority Inversion: If busy-waiting thread has higher priority than thread
holding lock ⇒ problem!
● Should sleep if waiting for a long time
● For longer runtimes, need to modify P and V so that
processes can block and resume.
49
Semaphore Implementation with no
Busy waiting
●
●
●
●
●
●
● typedef struct{
int value;
struct process *list;
} semaphore;
50
Implementation with no Busy waiting
(Cont.)
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);
}
}
51
Deadlock and Starvation
● Deadlock - two or more processes are waiting indefinitely for
an event that can be caused by only one of the waiting
processes.
● Let S and Q be semaphores initialized to 1
P0 P1
wait(S); wait(Q);
wait(Q);
.. wait(S);
..
. .
signal (S) ; signal (Q);
signal (Q); signal (S);
● Starvation- indefinite blocking. A process may never be
removed from the semaphore queue in which it is suspended.
52
Classical Problems of
Synchronization
53
Producer-Consumer with Bounded
Buffer Problem
● Shared data
● n buffers, each can hold one item
● Semaphore mutex initialized to the value 1
● Semaphore full initialized to the value 0
● Semaphore empty initialized to the value n
54
Bounded Buffer Problem
55
Bounded Buffer Problem
56
Discussion
● ASymmetry
● Producer does: P(empty), V(full)
● Consumer does: P(full), V(empty)
57
Discussion
● ASymmetry
● Producer does: P(empty), V(full)
● Consumer does: P(full), V(empty)
● Is order of P’s important between P(mutex) and P(empty or full)?
58
Discussion
● ASymmetry
● Producer does: P(empty), V(full)
● Consumer does: P(full), V(empty)
● Is order of P’s important between P(mutex) and P(empty or full)?
● Yes! Can cause deadlock
● Is order of V’s important?
59
Discussion
● ASymmetry
● Producer does: P(empty), V(full)
● Consumer does: P(full), V(empty)
● Is order of P’s important between P(mutex) and P(empty or full)?
● Yes! Can cause deadlock
● Is order of V’s important?
● No, except that it might affect scheduling efficiency
60
Readers-Writers Problem
W
R
R
R
61
Readers-Writers Problem
W
R
R
R
62
Readers-Writers Problem
● Shared Data
● Data set
● Semaphore rw_mutex initialized to 1
● Semaphore mutex initialized to 1
● Integer read_count initialized to 0
● The structure of a writer process
do {
wait(rw_mutex);
...
/* writing is performed */
...
signal(rw_mutex);
} while (true);
63
Readers-Writers Problem
● The structure of a reader process
do {
wait(mutex);
read_count++;
if (read_count == 1)
wait(rw_mutex);
signal(mutex);
...
/* reading is performed */
...
wait(mutex);
read count--;
if (read_count == 0)
signal(rw_mutex);
signal(mutex);
} while (true);
64
Dining-Philosophers Problem
65
Dining Philosophers Problem
● The structure of Philosopher i:
do {
wait (chopstick[i] );
wait (chopstick[ (i + 1) % 5] );
// eat
signal (chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
66
Dining Philosophers Problem
● The structure of Philosopher i:
do {
wait (chopstick[i] );
wait (chopstick[ (i + 1) % 5] );
// eat
signal (chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
● What is the problem with this algorithm?
67
Dining Philosophers Problem
● The structure of Philosopher i:
do {
wait (chopstick[i] );
wait (chopstick[ (i + 1) % 5] );
// eat
signal (chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
● What is the problem with this algorithm? Can result in a deadlock
68
Dining Philosophers Problem
● Deadlock handling
● Allow at most 4 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.
69
Problems with Semaphores
monitor monitor-name
{
// shared variable declarations
procedure P1 (…) { …. }
● condition x, y;
● Two operations are allowed on a condition variable:
● x.wait() a process that invokes the operation is
suspended until x.signal()
● x.signal() resumes one of processes (if any) that
invoked x.wait()
- If no x.wait() on the variable, then it has no effect on the
variable
Monitor with Condition Variables
Condition Variables Choices
initialization_code() {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
}
}
Solution to Dining Philosophers (Cont.)
DiningPhilosophers.pickup(i);
EAT
DiningPhilosophers.putdown(i);