0% found this document useful (0 votes)
5 views6 pages

Locks and Semaphores Ch4

The document discusses various synchronization mechanisms in operating systems, focusing on mutual exclusion, mutex locks, and semaphores. It explains the implementation of locks using atomic operations and the challenges of busy waiting, as well as the concepts of deadlock and starvation. Additionally, it presents practical examples such as the bounded buffer problem and the readers-writers problem, highlighting the importance of proper synchronization to avoid issues in concurrent programming.

Uploaded by

Anthony D
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)
5 views6 pages

Locks and Semaphores Ch4

The document discusses various synchronization mechanisms in operating systems, focusing on mutual exclusion, mutex locks, and semaphores. It explains the implementation of locks using atomic operations and the challenges of busy waiting, as well as the concepts of deadlock and starvation. Additionally, it presents practical examples such as the bounded buffer problem and the readers-writers problem, highlighting the importance of proper synchronization to avoid issues in concurrent programming.

Uploaded by

Anthony D
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/ 6

2/28/2019

Mutual Exclusion with Swap


Initially, s == false;

entry () {
Locks and Semaphores bool spin = true;
Swap(spin, s);
while (spin)
Swap(spin, s);
}

exit() {
Some slides and/or pictures are adapted from
s = false;
• Operating System Concepts, 9th edition, by Silberschatz, Galvin, }
Gagne, John Wiley & Sons, 2013
• Lecture notes by Dr. Prof. John Kubiatowicz (Berkeley)
1 2

1 2

Mutex Locks Naïve use of Interrupt Enable/Disable


• OS designers build software tools to solve
How can we build multi-instruction atomic operations?
critical section problem. Simplest is mutex lock
• Recall: dispatcher gets control in two ways.
• Protect a critical section by first acquire() a
– Internal: Thread does something to relinquish the CPU
lock then release() the lock
– External: Interrupts cause dispatcher to take CPU
• Boolean variable indicating if lock is available or not
• On a uniprocessor, can avoid context-switching by:
• Calls to acquire() and release() must be
– Avoiding internal events (although virtual memory tricky)
atomic
– Preventing external events by disabling interrupts
• Usually implemented via hardware atomic instructions
such as TestAndSet, swap, etc.
• But this solution requires busy waiting Consequently, naïve Implementation of locks:
• This lock therefore called a spinlock LockAcquire { disable Ints; }
LockRelease { enable Ints; }

3 4

Naïve use of Interrupt Enable/Disable: Problems Better Implementation of Locks by Disabling Interrupts
Key idea: maintain a lock variable and impose mutual exclusion only
during operations on that variable
Can’t let user do this! Consider following:
LockAcquire();
While(TRUE) {;} int value = FREE;

Real-Time system—no guarantees on timing! Acquire() { Release() {


disable interrupts; disable interrupts;
• Critical Sections might be arbitrarily long
if (value == BUSY) { if (anyone on wait queue) {
What happens with I/O or other important events? put thread on wait queue; take thread off wait queue
Go to sleep(); Place on ready queue;
• “Reactor about to meltdown. Help?” // Enable interrupts? } else {
value = FREE;
} else {
}
value = BUSY; enable interrupts;
} }
enable interrupts;
}

5 6

1
2/28/2019

New Lock Implementation: Discussion Interrupt Re-enable in Going to Sleep


• Why do we need to disable interrupts at all?
– Avoid interruption between checking and setting lock value
• What about re-enabling ints when going to sleep?
Acquire() {
– Otherwise two threads could think that they both have lock
disable interrupts;
Acquire() { if (value == BUSY) {
disable interrupts; put thread on wait queue;
if (value == BUSY) { Go to sleep();
put thread on wait queue; } else {
Go to sleep();
// Enable interrupts?
Critical value = BUSY;
}
} else { Section enable interrupts;
value = BUSY; }
}
enable interrupts;
}
• Note: unlike previous solution, the critical section (inside
Acquire()) is very short
– User of lock can take as long as they like in their own critical section:
doesn’t impact global machine behavior
– Critical interrupts taken in time!

7 8

Interrupt Re-enable in Going to Sleep Interrupt Re-enable in Going to Sleep


• What about re-enabling ints when going to sleep? • What about re-enabling ints when going to sleep?
Acquire() { Acquire() {
disable interrupts; disable interrupts;
if (value == BUSY) { if (value == BUSY) {
Enable Position put thread on wait queue; Enable Position put thread on wait queue;
Go to sleep(); Go to sleep();
} else { } else {
value = BUSY; value = BUSY;
} }
enable interrupts; enable interrupts;
} }
• Before Putting thread on the wait queue? • Before Putting thread on the wait queue?
– Release can check the queue and not wake up thread

9 10

Interrupt Re-enable in Going to Sleep Interrupt Re-enable in Going to Sleep


• What about re-enabling ints when going to sleep? • What about re-enabling ints when going to sleep?
Acquire() { Acquire() {
disable interrupts; disable interrupts;
if (value == BUSY) { if (value == BUSY) {
put thread on wait queue; put thread on wait queue;
Enable Position Go to sleep();
Enable Position Go to sleep();
} else { } else {
value = BUSY; value = BUSY;
} }
enable interrupts; enable interrupts;
} }
• Before Putting thread on the wait queue? • Before Putting thread on the wait queue?
– Release can check the queue and not wake up thread – Release can check the queue and not wake up thread
• After putting the thread on the wait queue • After putting the thread on the wait queue
– Release puts the thread on the ready queue, but the thread still thinks it
needs to go to sleep
– Misses wakeup and still holds lock (deadlock!)

11 12

2
2/28/2019

Interrupt Re-enable in Going to Sleep Interrupt Re-enable in Going to Sleep


• What about re-enabling ints when going to sleep? • What about re-enabling ints when going to sleep?
Acquire() { Acquire() {
disable interrupts; disable interrupts;
if (value == BUSY) { if (value == BUSY) {
put thread on wait queue; put thread on wait queue;
Go to sleep(); Go to sleep();
Enable Position Enable Position
} else { } else {
value = BUSY; value = BUSY;
} }
enable interrupts; enable interrupts;
} }
• Before Putting thread on the wait queue? • Before Putting thread on the wait queue?
– Release can check the queue and not wake up thread – Release can check the queue and not wake up thread
• After putting the thread on the wait queue • After putting the thread on the wait queue
– Release puts the thread on the ready queue, but the thread still thinks it – Release puts the thread on the ready queue, but the thread still thinks it
needs to go to sleep needs to go to sleep
– Misses wakeup and still holds lock (deadlock!) – Misses wakeup and still holds lock (deadlock!)
• Want to put it after sleep(). But – how?

13 14

How to Re-enable After Sleep()? Better Locks using test&set


• In scheduler, since interrupts are disabled when you call sleep: • Can we build test&set locks without busy-waiting?
– Responsibility of the next thread to re-enable ints – Can’t entirely, but can minimize!
– When the sleeping thread wakes up, returns to acquire and re-enables – Idea: only busy-wait to atomically check lock value
interrupts int guard = 0;
Thread A Thread B int value = FREE;
.
. Acquire() { Release() {
disable ints // Short busy-wait time
sleep // Short busy-wait time
while (test&set(guard)); while (test&set(guard));
sleep return if anyone on wait queue {
enable ints if (value == BUSY) {
take thread off wait queue
. put thread on wait queue; Place on ready queue;
. go to sleep() & guard = 0; } else {
. } else { value = FREE;
disable int value = BUSY; }
sleep guard = 0; guard = 0;
sleep return }
enable ints }
. • Note: sleep has to be sure to reset the guard variable
.
– Why can’t we do it just before or just after the sleep?

15 16

Locks using Interrupts vs. test&set


Compare to “disable interrupt” solution Semaphores (Dijkstra)
int value = FREE;

Acquire() { Release() {
• Synchronization tool that does not require
disable interrupts; disable interrupts; busy waiting.
if (value == BUSY) { if (anyone on wait queue) {
put thread on wait queue; take thread off wait queue • Semaphore is an object contains a
Place on ready queue;
Go to sleep();
// Enable interrupts? } else { (private) integer value and 2 operations.
value = FREE;
} else {
} – P operation, also called Down or Wait
value = BUSY;
}
enable interrupts; • Acquire a resource
}
enable interrupts; – V operation, also called Up or Signal
}
• Release a resource
Basically replace • Semaphores are “resource counters”.
– disable interrupts  while (test&set(guard));
– enable interrupts  guard = 0;
18

17 18

3
2/28/2019

Semaphore Implementation
• Semaphore from railway analogy
– Here is a semaphore initialized to 2 for • Define a semaphore as a record
resource control: typedef struct {
int value;
struct process *L;
} semaphore;

• Assume two simple operations:


– block(), suspends the process that invokes it.
– wakeup(P), changes the thread from the waiting state
to the ready state.
Value=2
Value=0
Value=1
20

19 20

Implementation Critical Sections with Semaphores


• Semaphore operations now defined as
wait(S): semaphore mutex = 1;
S.value--;
if (S.value < 0) { entry()
add this process to S.L;
block(); wait(mutex);
} exit()
signal(S): signal(mutex);
S.value++;
if (S.value <= 0) {
remove a process P from S.L; • For mutual exclusion, initialize
wakeup(P); semaphore to 1.
}
• The wait and signal operations are atomic.
21 22

21 22

Semaphore as a General
Bounded Buffer Problem
Synchronization Tool
• Execute B in Pj only after A executed in Pi • There is one Buffer object used to pass objects
from producers to consumers. The problem is
• Use semaphore flag initialized to 0
to allow concurrent access to the Buffer by
• Code: producers and consumers, while ensuring that
Pi Pj – Consumer must wait for producer to fill
buffers, if none full (scheduling constraint)
  – Producer must wait for consumer to empty
A wait(flag) buffers, if all full (scheduling constraint)
– Only one thread can manipulate buffer queue
signal(flag) B at a time (mutual exclusion)

23 24

23 24

4
2/28/2019

Bounded Buffer Bounded Buffer


(1 producer, 1 consumer) (multiple producers and consumers)

char buf[n], int front = 0, rear = 0; char buf[n], int front = 0, rear = 0;
semaphore empty = n, full = 0; semaphore empty = n, full = 0, mutex = 1;
Producer() Consumer ()
Producer() Consumer () while (1) { while (1) {
while (1) { while (1) { produce message m; wait(full);
produce message m; wait(full); wait(empty); wait(mutex);
wait(empty); m = buf[front]; wait(mutex); m = buf[front];
buf[rear] = m; front = front “+” 1; buf[rear] = m; front = front “+” 1;
rear = rear “+”1; signal(empty); rear = rear “+”1; signal(mutex);
signal(full); consume m; signal(mutex); signal(empty);
} } signal(full) consume m;
} }
25 26

25 26

Deadlock and Starvation Readers-Writers Problem


• Deadlock – two or more processes are waiting indefinitely for
an event that can be caused by only one of the waiting • Given a database
processes.
Let S and Q be two semaphores initialized to 1 Can have multiple “readers” at a time
P0 P1
wait(S); wait(Q); don’t ever modify database
wait(Q); wait(S); Only one “writer”
 
signal(S); signal(Q); will modify database
signal(Q); signal(S);
• Starvation – indefinite blocking. A process may never be
removed from the semaphore queue in which it is suspended.
• Priority Inversion – Scheduling problem when lower-priority • The problem has many variation
process holds a lock needed by higher-priority process
– Solved via priority-inheritance protocol

27 28

27 28

• Reader Process
Readers-Writers Problem readEnter () {
Semaphore Solution wait(mutex);
readcount++;
• Shared data if (readcount == 1)
wait(wrt);
semaphore mutex = 1, wrt = 1; signal(mutex);
int readcount = 0;
}
• Writer Process readExit() {
writeEnter () {
wait(wrt); wait(mutex);
} readcount--;
if (readcount == 0)
writeExit () { signal(wrt);
signal(wrt); signal(mutex);
} }

29 30

29 30

5
2/28/2019

Dining-Philosophers Problem Dining-Philosophers Problem


• Philosopher i:
do {
wait(chopstick[i])
wait(chopstick[(i+1) % 5])

eat

signal(chopstick[i]);
signal(chopstick[(i+1) % 5]);

think

• Shared data } while (1);
semaphore chopstick[5];
Initially all values are 1 31 32

31 32

Dining-Philosophers Problem Problems with Semaphores


(Deadlock Free)
• Philosopher i:
do { • Used for 2 independent purposes
if (i ! = 0) {
wait(chopstick[i]); wait(chopstick[i+1]);
– Mutual exclusion
… eat … – Condition Synchronization
signal(chopstick[i]); signal(chopstick[i+1]);
} else { • Hard to get right
wait(chopstick[1]); wait(chopstick[0]);
… eat … – Small mistake easily leads to deadlock
signal(chopstick[1]); signal(chopstick[0]);
}

think May want to separate mutual exclusion,

} while (1);
condition synchronization
33 34

33 34

You might also like