Semaphores - PowerPoint PPT Presentation

1 / 22
About This Presentation
Title:

Semaphores

Description:

A consumer wants to consume the buffer but cannot obtain the mutex lock. Producer #1 ... It is up to the thread scheduler which Y/Z to pick. ... – PowerPoint PPT presentation

Number of Views:189
Avg rating:3.0/5.0
Slides: 23
Provided by: andreaarpa
Category:
Tags: how | lock | pick | semaphores | to

less

Transcript and Presenter's Notes

Title: Semaphores


1
Semaphores
UNIVERSITY of WISCONSIN-MADISONComputer Sciences
Department
CS 537Introduction to Operating Systems
Andrea C. Arpaci-DusseauRemzi H.
Arpaci-Dusseau Haryadi S. Gunawi
  • Questions answered in this lecture
  • Why are semaphores necessary?
  • How are semaphores used for mutual exclusion?
  • How are semaphores used for scheduling
    constraints?
  • Examples Join and Producer/Consumer and
    Readers/Writers

2
Motivation for Semaphores
  • So far Locks only provide mutual exclusion
  • Ensure only one thread is in critical section at
    a time
  • L?lock can only be true/false (hence, some
    limitations exist)
  • Cannot solve all real-world problems
  • May want more Place ordering on scheduling of
    threads
  • Examples
  • Producer/Consumer
  • Producer Creates a resource (data)
  • Consumer Uses a resource (data)
  • ps grep gcc wc
  • thread_join()
  • Dont want producers and consumers to operate in
    lock step
  • Place a fixed-size buffer between producers and
    consumers
  • Synchronize accesses to buffer
  • Producer waits if buffer full consumer waits if
    buffer empty
  • No point to run producer threads if buffer is
    full
  • No point to run consumer threads if buffer is
    empty

3
Semaphores
  • Semaphores Introduced by Dijkstra in 1960s
  • Semaphores have two purposes
  • Mutex
  • Ensure threads dont access critical section at
    same time
  • Scheduling constraints
  • Ensure threads execute in specific order

4
Semaphore Operations
  • Allocate and Initialize
  • Semaphore initially contains a non-negative
    integer value
  • User cannot read or write value directly after
    initialization
  • There are only 3 operations init, wait and
    signal
  • Sem_t sem
  • Int sem_init(sem, is_shared, init_value)
  • Wait or Test
  • P() for test in Dutch (proberen)
  • Waits until value of sem is gt 0, then decrements
    sem value
  • Int sem_wait(sem)
  • Signal or Increment or Post
  • V() for increment in Dutch (verhogen)
  • Increments value of semaphore
  • Int sem_post(sem)

5
Semaphore Implementation
  • typedef struct
  • int value
  • queue tlist
  • semaphore
  • wait (semaphore S) // Must be executed
    atomically
  • S-gtvalue--
  • if (S-gtvalue lt 0)
  • add this process to S-gttlist
  • block()
  • signal (semaphore S) // Must be executed
    atomically
  • S-gtvalue
  • if (S-gtvalue lt 0)
  • remove thread t from S-gttlist
  • wakeup(t)

6
Semaphore Example
  • What happens if sem is initialized to 2?
  • Scenario Three threads call wait(sem)
  • Two threads can proceed
  • One thread will be blocked (current S.value -1)
  • Observations
  • Initial sem value is positive ? Number of threads
    that can be in c.s. at same time
  • Current sem value is negative ? Number of waiters
    on queue currently

7
Ex1 Mutual Exclusion with Semaphores
  • Previous example with lock L
  • lock(L)
  • balance amount
  • unlock(L)
  • Example with semaphore S
  • wait(S)
  • balance amount
  • signal(S)
  • To what value should S be initialized? 1
  • Whenever you initialize a semaphore value to 1,
    it will work like a lock (I.e. Init(S,1) ? lock
    L)
  • What happens if S is initialized to 0?
  • No one will be able enter the C/S, the system is
    deadlock

8
Binary Semaphores
  • Binary semaphore is sufficient for mutex
  • Binary semaphore has boolean value (not integer)
  • bsem_wait() Waits until value is 1, then sets to
    0
  • bsem_signal() Sets value to 1, waking one
    waiting process
  • General semaphore is also called counting
    semaphore

9
Ex2 Scheduling Constraints
  • General case One thread waits for another to
    reach some point
  • Example Implement thread_join()
  • Parent thread creates 3 threads (if a child
    thread finishes, it will call exit())
  • Parent thread should not exit before all child
    threads finish. Hence, parent thread calls
    thread_join() three times
  • Lets say we have a shared sem S between parent
    and child (created when child thread is created)
  • To what value is S initialized? 0
  • How about if S is initialized to 1?
  • The parent thread will finish before the last
    child thread exits

Child thread exit() signal(sem)
Parent thread Thread_join() wait(sem)
10
Ex3 Producer/Consumer Single Buffer
  • Simplest case
  • Single producer thread, single consumer thread
  • Single shared buffer between producer and
    consumer
  • Requirements
  • Consumer must wait for producer to fill buffer
  • Producer must wait for consumer to empty buffer
    (if filled)
  • Requires 2 semaphores
  • empty Initialize to 1
  • full Initialize to 0
  • What happens if
  • empty is initialized to 0 produce can never
    start filling the buffer
  • full is initialized to 1 consumer will consume a
    garbage buffer (the buffer that hasnt been
    filled)

Producer While (1) wait(empty) Fill(buffer)
signal(full)
Consumer While (1) wait(full) Use(buffer)
signal(empty)
11
Ex4 Prod/Cons Circular Buffer
  • Next case
  • Single producer thread, single consumer thread
  • Shared buffer with N elements between producer
    and consumer
  • Requirements
  • Consumer can run as long as there is at least one
    filled element
  • Producer must wait for if all elements are filled
  • Requires 2 semaphores
  • empty Initialized to N (denotes how many empty
    buffers)
  • full Initialized to 0 (denotes how many used
    buffers)
  • What happens if
  • empty is initialized to 1 the producer can only
    fill one element (which implies the consumer can
    only use one element too)

Consumer j 0 While (1) wait(full) Use(bu
fferj) j (j1)N signal(empty)
Producer i 0 While (1) wait(empty) Fill
(bufferi) i (i1)N signal(full)
12
Ex4 Producer/Consumer Multiple Threads
  • Final case
  • Make the last case in the previous slide faster
  • In the last case, we have N elements, but only
    one producer thread
  • Make multiple producer threads, multiple consumer
    threads
  • Shared buffer with N elements between producer
    and consumer
  • Requirements
  • A consumer thread can run if there is a filled
    element
  • A producer thread must wait if buffer is full
  • Each consumer thread must grab a unique filled
    element
  • Each producer thread must grab a unique empty
    element
  • Any problem?

Producer While (1) a) wait(empty) b) i
findempty(buffer) c) Fill(bufferi) d) signal(
full)
Consumer While (1) wait(full) j
findfull(buffer) Use(bufferj) signal(empty
)
13
Ex4 (contd)
  • The code in the previous slide is broken
  • Race condition could occur
  • Example 3 producer threads running the same time
  • Initially empty is N (say N is 10)
  • T1 (a) (b) ltpreemptedgt
  • Now empty is 9, and T1 finds element 0 (i 0)
    is free and needs to be filled
  • T2 (a) (b) ltpreemptedgt
  • Now empty is 8, and T2 will fill element 0 too
  • T3 (a) (b) ltpreemptedgt
  • Now empty is 7, and T3 will fill element 0 too
  • Problem
  • Critical section for findempty and Fill is
    not guarded with mutex semaphore, hence race
    condition happens
  • When all three producer threads are done, the
    free semaphore will be 3, implying there are 3
    elements that have been filled (but actually
    there is only one)
  • Observations
  • empty and full semaphores are scheduling
    semaphores (they do not guarantee mutual
    exclusion)
  • Hence, need a mutex semaphore (a semaphore
    initialized with 1)

14
Ex4 Solutions
  • Consider two possible solutions Which work?
  • mutex is initialized to 1
  • empty is initialized to N
  • full is initialized to 0

Producer Solution 1 wait(mutex) wait(empty)
i findempty(buffer) Fill(bufferi) signal
(full) signal(mutex)
Consumer Solution 1 wait(mutex) wait(full) j
findfull(buffer) Use(bufferj) signal(em
pty) signal(mutex)
Consumer Solution 2 wait(full) wait(mutex) j
findfull(buffer) Use(bufferj) signal(mu
tex) signal(empty)
Producer Solution 2 wait(empty) wait(mutex)
i findempty(buffer) Fill(bufferi) signal
(mutex) signal(full)
15
Ex4 Solution 1
Producer 1 wait(mutex) wait(empty) i
findempty(buffer) Fill(bufferi) signal(full
) signal(mutex)
Consumer 1 wait(mutex) wait(full) j
findfull(buffer) Use(bufferj) signal(empty
) signal(mutex)
  • Producer
  • Get the mutex lock, and then
  • Check if I can fill the buffer or not. If not,
    block me
  • Consumer
  • Get the mutex lock, and then
  • Check if I can empty the buffer or not. If not,
    block me.
  • Deadlock?
  • When the buffer is finally full, and a producer
    wants to fill the buffer again, it will be
    blocked on empty.
  • If blocked on empty, it means this producer is
    also holding the mutex lock
  • A consumer wants to consume the buffer but cannot
    obtain the mutex lock

16
Ex4 Solution 2 (the correct one)
Consumer 2 wait(full) wait(mutex) j
findfull(buffer) Use(bufferj) signal(mutex
) signal(empty)
Producer 2 wait(empty) wait(mutex) i
findempty(buffer) Fill(bufferi) signal(mute
x) signal(full)
  • Producer
  • Check first if can fill the buffer or not
  • If so, then get the mutex lock and fill the
    buffer
  • Consumer
  • Check first if can empty the buffer or not
  • If so, get the mutex lock and use the buffer
  • Observations
  • Be careful in terms of what you put inside the
    critical section

17
Ex5 Readers/Writers Problem
  • Problem
  • One or more threads read and write to a shared
    data
  • Basic concept
  • No writers all readers can read data without
    acquiring lock
  • Two writers not allowed. Hence, can only allow
    one writer at a time, which means a writer must
    acquire a lock before proceeding
  • Design
  • Need to count the number of readers (if zero, the
    writer can proceed)

18
  • Mutex 1
  • Wrt 1
  • Readcount 0
  • Write()
  • x) wait(wrt) // wait for any readers and
    writers
  • y) doWrite()
  • z) signal(wrt)
  • Read()
  • a) wait(mutex) // lock for ensuring mutex for
    readcount
  • b) readcount
  • c) if (readcount 1) // reader cannot proceed
    if writer
  • d) wait(wrt) // use the lock
  • e) signal(mutex)
  • f) doRead()

19
Ex5 Observations
  • A writer is running
  • If there is a writer, the first reader cannot
    proceed.
  • The first reader holds the mutex lock so no
    other readers can proceed
  • A writer enters
  • If a writer enters but some readers still exist,
    the last reader will signal the wrt.
  • One of the threads that is waiting on the wrt
    thread can proceed (a reader or a writer can wait
    on wrt)
  • E.g. a writer X is running, a writer Y and a
    reader Z wants to enter. After X is done. It is
    up to the thread scheduler which Y/Z to pick. If
    FIFO, then the first one who is waiting will get
    to run.
  • Runtime example
  • Initially mutex1, wrt1, readcount0
  • R1 a b c d e f ltpreemptedgt mutex1, wrt0,
    readcount1
  • R2 a b c d e f ltpreemptedgt mutex1, wrt0,
    readcount2
  • W1 x ltblockedgt (why? Because mutex1, wrt-1,
    readcount2

20
Ex5 Problem?
  • Starvation?
  • Is there a case of starvation? Is there a
    scenario where a writer never runs?
  • Initially mutex1, wrt1, readcount0
  • R1 a b c d e f ltpreemptedgt mutex1, wrt0,
    readcount1
  • R2 a b c d e f ltpreemptedgt mutex1, wrt0,
    readcount2
  • W1 x ltblockedgt
  • R3, R4, R5 comes in and go upto (f) mutex1,
    wrt0, readcount5
  • R1 g h i j k mutex1, wrt0, readcount4
  • R2 g h I j k mutex1, wrt0, readcount3
  • Although W1 comes in before R3, R4, and R5, W1
    can only proceed if there is no readers in the
    system (i.e. readcount must go to 0 ?
    signal(wrt)).
  • As long as readcount does not go to zero, the
    writer can never run ? Starvation
  • The problem is there is no queue (semaphore
    queue) which maintains the ordering of arrival
  • Solution
  • Add another semaphore (e.g. f)
  • All readers and writers must wait on f first such
    that all is queued properly in semaphore f
  • Ex f.queue R1 R2 W1 R3 R4 R5
  • New readers come after a writer (R3, R4 and R5)
    cannot read unless W1 has released f
  • Hence the goal is when a writer is waiting on
    wrt, it also holds an extra lock f

21
Ex5 Fair Readers/Writers
  • Mutex 1
  • Wrt 1
  • Readcount 0
  • F 1
  • Write()
  • wait(f)
  • wait(wrt) // While holding wrt, also hold f
    so that new readers cannot
  • // proceed before me!
  • signal(f) // If I have acquired the wrt lock,
    I can release f
  • doWrite()
  • signal(wrt)
  • Read()
  • wait(f) // all readers must go to f.queue
    first if there is a waiting writer
  • wait(mutex)
  • readcount

22
Next Time
  • Semaphore ? still ugly
  • Use Monitors.
  • But why still learn semaphore?
  • Monitors are only supported in high-level
    language such as Java.
  • Inside OS (still written in C), there is no such
    nice support. Hence, must use semaphore, lock,
    etc.
Write a Comment
User Comments (0)
About PowerShow.com