Title: Process Synchronization
1Process Synchronization
- Race Conditions
- Example Spooler directory with slots index
variable two processes attempt concurrent access
Process A
5
6
7
4
. . .
. . .
prog1
prog2
prog3
In7
Process B
Out 4
2Race Conditions...
- Process A reads in and is switched..
- Process B reads in, changes its contents to his
job and moves in by one - Process A gets the control and changes the
contents of his in to his job and moves the
value of in by one - Result in has a correct value but the
printing job of B is lost. - Comment common data structures to A, B
3Critical Sections
- Mutual Exclusion
- Critical section
- Conditions needed
- No two processes in critical sections
simultaneously - Process outside its critical section may not
block another process - There is a limit on the number of times a process
can enter the CS while another waits (i.e. no
starving) - No assumptions about speeds, no. of cpus, etc.
- 1st condition avoids race condition, but is not
sufficient for correct concurrent cooperation
4Critical Regions
- Mutual exclusion using critical regions
5Mutual Exclusion - attempts...
- 1. Disable interrupts
- Violates condition 3 or 2(forgot to enable)
- Too much responsibility for a user process
- 2. Lock variables
- If Lock 0 then set it to 1 and enter CS
- else wait...
- Race conditions occur when context is changed
between read 0 and write 1
6 another attempt
- 3. Strict alternation -
- while (TRUE)
- while (turn ! 0) / wait /
- critical_section()
- turn 1
- non_critical_section()
-
- Violation process blocked by another process
outside its critical section!!
- while (TRUE)
- while (turn ! 1) / wait /
- critical_section()
- turn 0
- non_critical_section()
-
7Petersons Solution
- define FALSE 0
- define TRUE 1
- define N 2
- int turn / whose turn is it ? /
- int interestedN / all initially 0
(FALSE) / - void enter_region(int process) / who is
entering 0 or 1 ? / -
- int other / number of other process /
- other 1- process / opposite of process
/ - interested(process) TRUE / signal that
youre interested / - turn process / set flag /
- while (turn process
- interested(other) TRUE) / null statement
/ -
- void leave_region(int process) / who is
leaving 0 or 1 ? / -
- interested(process) FALSE / departure from
critical region /
8For any Solution
- Need to Prove
- Mutual Exclusion - No two processes are in their
respective CS at the same time - Progress - a process outside its CS cannot block
a process waiting to enter a CS - Bounded waiting (no Starvation) - no process
waits indefinitely to enter its CS
9Petersons Solution - observations
- Solution for two processes only
- For N processes some numbering of the queue is
needed.. - Uses busy-wait
- Memory writes are assumed to be atomic
interested lt-- TRUE - a basic assumption for all solutions is
- No Process dies inside a critical section
10 and for N Processes ...
- int valueN / processes get waiting
numbers / - int busyN / just a flag... /
- void enter_region(int i ) / process i
entering.. / -
- busy(i) TRUE / guard the value selection
/ - value(i) max(value(0), value(1), ,
value(N-1)) 1 / LAST in line / - busy(i) FALSE
- for(k0 k lt N k )
- while (busy(k) TRUE) / wait before
checking / - while((value(k) ! 0) ((value(k), k) lt
(value(i), i))) / wait / -
-
- void leave_region(int i )
-
- value(i ) 0
11Using the Hardware - TSL instruction
- TSL - Test and Set Lock
- read a memory word into a register AND store a
nonzero value into that word, in an indivisible
instruction sequence - enter_region
- tsl R1,flag copy flag to R1 and set flag
to 1 - cmp R1,0 was flag 0 ?
- jnz enter_region if not zero, Lock was set, so
loop - ret return enter critical region
- leave_region
- mov flag,0 set flag to 0
- ret return critical region left
12Implement fairness with TSL
- test_and_set(int flag) - TSL 1,flag and
return(flag) -
- interested(i) TRUE
- test TRUE
- while(interested(i) TRUE test TRUE)
- test test_and_set(lock)
- interested(i) FALSE
-
- . . . critical section . . .
-
- j i1 n
- while(j ! i !(interested(j))) j n
- if(j i) lock FALSE
- else interested(j) FALSE
13Synchronization Constructs - Semaphores
- Two operations define a semaphore S
- DOWN(S) p(s)
- while(s lt 0)
- s s - 1
- UP(S) v(s)
- s s 1
- Operations on the counter are performed
indivisibly - S is non-negative
14Synchronizing with Semaphores
- Mutual Exclusion
- down(mutex)
- critical section
- up(mutex)
- Synchronization
- requirement S2 executed after S1 is completed
- Synch 0
- P1 S1 P2 DOWN(Synch)
- UP(Synch) S2
15 deadlocks with Semaphores...
- Two processes p0 and p1
- Two semaphores Q and S
- p0 p1
- down(S) down(Q)
- down(Q) down(S)
- . ..
- up(S) up(Q)
- up(Q) up(S)
- p0 does the first line, then p1 and so on...
16More on Synchronization...
- Three processes p1 p2 p3
- semaphores s1 1, s2 0
- p1 p2 p3
- down(s1) down(s2) down(s2)
- .. code .. .. code .. .. code ..
- up(s2) up(s2) up(s1)
- the ordering of the processes is (p1(p2p3))
- --gt ordering can be done by events (on a clock)
17Whats wrong with busy waiting
- Wastes cpu time by waiting
- Side effects
- Two processes with different priorities arrive at
their critical section in an order inverse to
their priorities - The higher priority process gets time-slices
- The lower priority process cannot complete its
processing of its critical section and leave ! - Priority Inversion
181st attempt - sleep and wakeup
- sleep() is a system call that blocks the calling
process indefinitely - wakeup(p) unblocks process p
- A process calls sleep() when it tries to enter
and finds that the lock is true - enter if (lock) call sleep()
- Each process calls wakeup(other) when leaving the
critical section... - leave lock false wakeup(other)
19Problems with sleep() and wakeup()
- Process 0 checks lock TRUE and is blocked
before putting itself to sleep - Process 1 generates a wakeup(process 0), after
leaving its critical section - The system call wakeup(process 0) is wasted
because process 0 is not yet sleeping - Process 0 is unblocked later and calls sleep()
- Process 1 has to go through its CS again, to
wakeup process 0
20Concrete problem - bounded buffer
- Two processes (at least) use a shared buffer in
memory - The buffer is finite (i.e. bounded)
- One process writes on the buffer and the other
process reads from it - A full buffer stops the writer (producer)
- An empty buffer stops the reader (consumer)
21Bounded Buffer with sleep(), wakeup()
- A producer process calls sleep() when the mutual
buffer is full - A consumer process calls sleep() when the mutual
buffer is empty - Each process calls wakeup(other) when some
condition on the mutual buffer is true
22Producer-consumer with sleep and wakeup
- define N 100 / size of buffer /
- int count 0 / number of items in buffer
/ - void producer(void)
-
- int item
- while(TRUE) / forever... /
- produce_item(item) / produce an item /
- if (count N) sleep() / go to sleep if
buffer is full / - enter_item(item) / insert item into buffer
/ - count count 1 / increment count of items
/ - if (count 1) wakeup(consumer) / was
buffer empty ? / -
23Producer-consumer with sleep and wakeup
- void consumer(void)
-
- int item
- while(TRUE) / forever... /
- if (count 0) sleep() / go to sleep if
buffer is empty / - remove_item(item) / remove item from
buffer / - count count - 1 / increment count of
items / - if (count N - 1) wakeup(producer) / was
buffer full ? / - consume_item(item) / print item /
-
24Race conditions with sleep() and wakeup()
- Consumer checks buffer0 and is blocked before
putting itself to sleep - Producer generates a wakeup(consumer), based on
an empty buffer - The system call wakeup(consumer) is wasted
because consumer is not yet sleeping - Consumer is unblocked later and calls sleep()
- Producer continues running and fills up the
buffer, then calls sleep(). - Both processes sleep forever
25Semaphores with blocking (Dijkstra 1965)
- An integer counter S
- Operation DOWN
- if S gt 0 decrement S,
- else sleep
- Operation UP
- If (S 0 there are sleeping processes) wake
one up (S remains 0) , - else increment S
- this semaphore has only nonnegative values
- comment UP and DOWN are atomic operations
26Negative-valued Semaphores
- An integer counter S
- Operation DOWN
- decrement S and check value, if S lt 0, sleep
- Operation UP
- increment S , if processes are sleeping (S lt 0)
, wake one up - a semaphore may have negative values
- the magnitude of the negative value is the
number of waiting processes - negative values arise because of the switching
of lines of decrement and check (from the
classical semaphore)
27Semaphores for the Producer-Consumer problem
- define N 100 / Buffer size /
- typedef int semaphore
- semaphore mutex 1 / access control to
critical section / - semaphore empty N / counts empty buffer slots
/ - semaphore full 0 / full slots /
- void producer(void)
- int item
- while(TRUE)
- produce_item(item) / generate something...
/ - down(empty) / decrement count of empty /
- down(mutex) / enter critical section /
- enter_item(item) / insert into buffer /
- up(mutex) / leave critical section /
- up(full) / increment count of full slots /
-
28Semaphores for the Producer-Consumer problem
- void consumer(void) int item while(TRUE)
down(full) / decrement count of full
/ down(mutex) / enter critical section
/ remove_item(item) / take item from buffer)
/ up(mutex) / leave critical section
/ up(empty) / update count of empty
/ consume_item(item) / do something...
/ Comment up() and down() are simple
atomic operations
29Semaphores for the mailboxes ...
- In the 1st assignment a thread using receive()
blocks on an empty mailbox and mailboxes have to
be protected for read/write - two semaphores (unbounded buffer)
- rw for read/write ne for non-empty
- Receive() send()
- down(ne) down(rw)
- down(rw) add_message
- read_message up(ne)
- up(rw) up(rw)
- .. ...
30Bounded-buffer for messages
- Threads use the system call send() to send
messages, which are then stored in threads
mailboxes (Shell mailbox) - The buffer of all mailboxes is bounded and so
send() is a producer and the receive() function
that extracts the messages from the threads
(Shell) mailboxes is a consumer - These two functions must use one common
semaphore, empty, initialized to the (user
defined) size of the buffer N. - The implementation of a bounded-buffer, makes
use of an ne semaphore for each thread
31Implementation of Semaphores
- struct semaphore
- int value, flag // flag is a variable for
testset - true when 0 - list_proc L
-
- DOWN(S) repeat until testset(S.flag)
- S.value S.value - 1
- if(S.value lt 0)
- add process to S.L
- set-up p as blocked
- S.flag 0
- --gt scheduler
- else S.flag 0
- UP(S) repeat until testset(S.flag)
- S.value S.value 1
- if(S.value lt 0)
- remove a process P from S.L
- wakeup(P)
- S.flag 0
32Atomicity of Semaphores
- The use of TSL can work for several cpus,
enabling access of only one cpu at a time to the
semaphore itself - For a single cpu one can simply use disable
interrupts - The use of disable interrupts is limited to
several lines of (system) code - acceptable for the system to do so
- locks with the TSL instruction - busy-waiting
33Implementation of Semaphores Disabling
Interrupts
- Wait(s)
- inhibit interrupts
- s.count s.count-1
- if s.countlt0
- then begin
- place this process in s.queue
- block this process and allow interrupts
- end
- else allow interrupts
- Signal(s)
- inhibit interrupts
- s.count s.count1
- if s.count 0
- then begin
- remove a process P from s.queue
- place process P on ready list
- end
- allow interrupts
34Semaphores Busy-Wait
- With previous solutions a process was
Busy-waiting during the Entire critical section
of another process - may be a long time! - Now a process is busy-waiting only during the
short CS of updating the semaphores counter!
35Counting and Binary semaphores
- binary-semaphore S1, S2
- down(S) up(S)
- down(S1) down(S1)
- S.value-- S.value
- if(S.value lt 0) if(S.value lt 0) up(S2)
- up(S1) up(S1)
- down(S2)
- else up(S1)
-
- This is the negative implementation of a
general semaphore
36Mutex binary semaphores
- In user space (Solaris) one can use TSL
- mutex_lock
- TSL REG, mutex
- CMP REG, 0
- JZE ok the opposite of enter_region
- CALL queue_thread or thread_yield
- ok
- RET
- mutex_unlock
- MOV mutex, 0
- RET
- queue_thread is a call to a user-space scheduler
and thus no kernel context switch is necessary.
In real life additional functions are supported,
like try_lock()
37Semaphores
- Binary vs. General Semaphores
- No or very little Busy-Wait
- Can be used to solve both Mutual exclusion and
Synchronization problems - Need to be careful to avoid Deadlocks
- The shared variable problem
38Choices of the New Generation
- Load-linked/store-conditional(DECs Alpha AXP,
IBMs PowerPC, MIPS R4000) - Two complementary operationsRead from address
XIf there was no write to X since your last
LL(X)Write V on Xelse FailMore power to LL/SC
then TSLPossible to implement atomic
incrementPossible to identify multiple writes of
the same value
39Implementing Locks with LL/SC
- Use variable L(0 free, 1 used)
- Lock-Acquire(L)
- A LL(L) if L then goto A if SC(L,1) then
goto A - Lock-Release(L)
- L 0
40Event Counters
- Integer counters with three operations
- Advance(E) increment (atomically) E by 1 wake
up relevant sleepers - Await(E,v) wait until E gt v. sleep if E lt v.
- Read(E) returns the current value of E
- Only increase, never decrease
- For the EC implementation of a bounded-buffer
problem no Read() is needed. Only for absolute
synchronization
41producer-consumer with Event Counters
- define N 100typedef int event_counter event
_counter in 0 / counts inserted items
/event_counter out 0 / items removed from
buffer /void producer(void)int item,
sequence 0 - while(TRUE) produce_item(item) sequence
sequence 1 / counts items produced
/ await(out, sequence - N) / wait until
buffer has room / enter_item(item) / insert
into buffer / advance(in) / inform
consumer /
42Event counters (producer-consumer)
- void consumer(void) int item, sequence
0 while(TRUE) sequence sequence
1 / count items produced /
await(in, sequence) / wait for item
/ remove_item(item) / take item from
buffer / advance(out) /
inform producer / consume_item(item)
43Message Passing - avoiding former problems
- For several cpus with their own memories
semaphores cannot provide mutual exclusion - Implement synchronization by system calls
- Issues
- Sometimes an acknowledgement is needed
- A reliable address for processes (domains..)
- message ID to avoid duplication
- Authentication (validate the senders ID)
- Two main functions
- send(destination, message)
- receive(source, message) block while
waiting... - To avoid accessing a processs address space -
mailboxes
44Producer-consumer with Message Passing
- define N 100
- define MSIZE 4 / message size /
- typedef int message(MSIZE)
- void producer(void)
- int item
- message m / message buffer /
- while(TRUE) produce_item(item)
- receive(consumer, m) /wait for an empty /
- construct_message(m, item)
- send(consumer, m) / send item /
-
-
45Message passing (cont.)
- void consumer(void)int item, imessage
mfor(i 0 i lt N i) send(producer, m)
/ send N empties / / Storage is up to the
OS / - while(TRUE) receive(producer, m) / get
message with item / extract_item(m,
item) send(producer, m) / send an empty
reply / consume_item(item)
46Messages - comments
- Unix pipes - a generalization of messages no
fixed size message (blocking receive) - Mailboxes take on the responsibility of
maintaining the buffer.. - Mailboxes can serve as an alternative address for
a process (instead of PID) - If no buffer is maintained by the system, then
every receive can only be run after a send call
this Rendezvous
47Equivalence Message passing with Semaphores
- Each process has an associated semaphore,
initially 0, on which to block while waiting for
a send or receive - A shared buffer area contains mailboxes, each one
containing an array of message slots - Slots are chained together, to keep their
receiving order, and there are counters of full
slots and of empty slots - Mailboxes also contain a pointer to queue of
unable_to_send_to processes and a pointer to
queue of unable_to_receive_from - This orders up operations for the waiting process
in a queue - The whole shared buffer must be protected by a
binary semaphore, so that only one process can
inspect or update the shared data structures
48Message passing with Semaphores
- Performing a send on a mailbox that contains at
least one empty slot - inserts a message, updates
counters and links - Performing a receive on an empty mailbox - enters
itself on the receive queue, up on mutex, down on
its own semaphore - When the receiving process is awakened - down on
mutex - Performing a send that has an empty slot - after
inserting the message, the sender checks the
waiting queue, if not empty removes the first
process and performs an up on its semaphore - The sender leaves the critical region following
the above up operation, and the mutex semaphore
is treated very similarly to the example
implementation of monitors. - Performing a send to a full mailbox - enters
itself on the send queue, up on mutex, down on
its own semaphore
49Equivalence Semaphores with Message passing
- use a semaphore-process (mailbox) p
- p keeps the counter of the semaphore s
- p1 down(s) - send(p,down(s))
- receive(p,ack)
- if s lt 0 p does not send an acknowledgement
- P2 up(s) - send(p,up(s))
- if s lt 0 p sends the acknowledgement to a
selected process send (pi, ack) - Advantage porting code to the distributed
world
50Monitors - high level synchronization constructs
- Semaphores and event-counters are too primitive
(low level) and are hard to program - Monitors are a special package of procedures
- Mutual exclusion constructs are generated by the
compiler. Internal data structures are invisible - Only one process is active in a monitor at the
same time - high level mutual exclusion - monitor sharedData
- int buffer
- public
- writeData(int byteNum)
-
- readData(int byteNum)
-
51- type monitor-name monitor
- variable declarations
- procedure entry P1 ()
- begin end
- procedure entry P2 ()
- begin end
- .
- .
- .
- procedure entry Pn ()
- begin end
- begin
- initialization code
- end
52Monitors - Condition variables
- Only one process is active in a monitor at the
same time - high level mutual exclusion - To enable synchronization Condition variables
and operations on them wait and signal - the monitor provides queuing for waiting
procedures - When one procedure waits and another signals,
the signaling procedure is inside the monitor !!!
- Operation signal must be either followed by
block() or exit_monitor, so that only one
procedure is active at one time
53Bounded-Buffer with Monitors
- monitor ProducerConsumer
- condition full, empty
- integer count
- procedure enter
- begin if count N then wait(full)
- enter_item
- count count 1
- if count 1 then signal(empty) end
- procedure remove
- begin if count 0 then wait(empty)
- remove_item
- count count - 1
- if count N - 1 then signal(full) end
- count 0
- end monitor
54Bounded-Buffer with Monitors (II)
- procedure producer
- begin while true do
- begin
- produce_item
- ProducerConsumer.enter
- end
- end
- procedure consumer
- begin while true do
- begin
- ProducerConsumer.remove
- consume_item end
- end
55Monitors - some comments
- Condition variables do not accumulate signals,
for later use - wait() must come before signal()
- Unlike sleep() and wakeup(), no race conditions,
because monitors have mutual exclusion - More complex implementation than semaphores,
compilers construct instead of system calls Up
Down - Why not implemented?
- How to interpret nested monitors ?
- How to define wait, priority scheduling,
timeouts, aborts ? - How to Handle all exception conditions ?
- How to interact with process creation and
destruction ?
56Monitors vs. Java
- Solution to producer-consumer problem in Java
(part 1)
57Monitors vs. Java (Cont.)
- Solution to producer-consumer problem in Java
(part 2)
58Equivalence Implementing Monitors with
Semaphores - problems
- typedef int semaphore
- semaphore mutex1 /control access to monitor/
- void enter_monitor(void)
- down(mutex) /only one-at-a-time/
-
- void leave(void)
- up(mutex) /allow other processes in/
-
- void leave_with_signal(semaphore c) /c
signals for leave/ - up(c) /of the condition variable/
-
- Void wait(semaphore c) /block on a condition/
- /c is the condition/
- up(mutex) /allow other processes/
- down (c) /block on the condition/
59Implementing Monitors with SemaphoresCorrect!
- semaphore mutex 1 / control access to
monitor / - cond c / c countsemaphore /
- void enter_monitor(void)
- down(mutex) / only one-at-a-time /
-
- void leave(void)
- up(mutex) / allow other processes in /
-
- void leave_with_signal(cond c) / cond
c is a struct / - if(c.count 0) up(mutex) / no waiting,
just leave.. / - else c.count--
- up(c.s)
-
- void wait(cond c) / block on a condition
/ - c.count / count waiting processes /
- up(mutex) / allow other processes /
- down(c.s) / block on the condition /
60Barriers
- Use of a barrier
- processes approaching a barrier
- all processes but one blocked at barrier
- last process arrives, all are let through
61Dining Philosophers (1)
- Philosophers eat/think
- Eating needs 2 forks
- Pick one fork at a time
- How to prevent deadlock
- What about Starvation?
62The dining (chinese) philosophers - problem
- Each process needs two resources
- Every resource is mutual to two processes - i.e.
every pair of processes compete for a specific
resource - Every process can either be assigned two
resources or none at all - Every process that is waiting for its two
resources should sleep (be blocked) - Every process that releases its two resources
must wake-up the two competing processes for
these resources, if they are interested.
63The dining (chinese) philosophers - problem
- define N 5
- void philosophers(int i)
-
- while(TRUE)
- think()
- take_stick(i) / left stick /
- take_stick(i1) N) / right stick /
- eat()
- put_stick(i)
- put_stick((i1) N)
-
-
- replace take_stick(i) by down(i) put_stick(I)
by up(I)
64Dining philosophers (soltn.)
- define N 5
- define LEFT (i-1) N
- define RIGHT (i1) N
- define THINKING 0
- define HUNGRY 1
- define EATING 2
- typedef int semaphore
- int stateN
- semaphore mutex 1
- semaphore sN / per each philosopher /
- void philosopher(int i)
-
- while(TRUE)
- think()
- pick_sticks(i)
- eat()
- put_sticks(i)
-
65pick_sticks(i) put_sticks(i) test(i)
- void pick_sticks(int i)
- down(mutex) / enter CS /
- statei HUNGRY
- test(i) / try for 2 sticks /
- up(mutex) / exit CS /
- down(si) / block if sticks were not
acquired../ -
- void put_sticks(int i)
- down(mutex)
- statei THINKING / finished eating../
- test(LEFT) / can left neighbour eat now ? /
- test(RIGHT) / .. RIGHT.. ? /
- up(mutex)
-
- void test(int i)
- if(statei HUNGRY stateLEFT ! EATING
stateRIGHT ! EATING) - statei EATING
- up(si)
66Chinese Philosophers - Monitor
- monitor diningPhilosophers
- condition selfN
- integer stateN
- procedure pick_sticks(i)
- begin statei HUNGRY
- test(i)
- if statei ltgt EATING then wait(selfi)
- end
- procedure put_sticks(i)
- begin
- statei THINKING
- test(LEFT)
- test(RIGHT)
- end
67Chinese Philosophers - Monitor (II)
- non-entry-procedure test(i)
- begin
- if stateLEFT ltgt EATING
- and stateRIGHT ltgt EATING
- and statei HUNGRY
- then begin
- statei EATING
- signal(selfi)
- end
- end
- for i 0 to 4 do statei THINKING
- end monitor
68Dining Philosophers Deadlocks Starvation
- the solution is deadlock-free because every
process has either two resources or none - for deadlock a process must have one resource
and block for the other that is owned by another
process (which is blocked waiting for the
first).. - Starvation is possible
- Process 1 2 3 4 5
- eating-state e e
- e e
- e e
- e e
69The Sleeping Barber Problem
70..yet another synchronization problem - the
sleeping barber
- barber shop - one service provider many
customers - Finite capacity of shop - finite waiting queue
- One customer is served at one time
- Service provider, barber, sleeps when no
customers are waiting - Customer leaves if shop is full
- Customer sleeps while waiting in queue
- Use two semaphores - barber waits for customers
customers wait for barbers count queue length..
71..yet another synchronization problem - the
sleeping barber
- define CHAIRS 5
- typedef int semaphore
- semaphore customers 0
- semaphore barbers 0
- semaphore mutex 1
- int waiting 0
- void barber(void)
- while(TRUE)
- down(customers) / block if no customers /
- down(mutex) / access to waiting /
- waiting waiting - 1
- up(barbers) / barber is in.. /
- up(mutex) / release waiting /
- cut_hair()
72The sleeping barber
- void customer(void) down(mutex) / enter
CS / if(waiting lt CHAIRS) waiting
waiting 1 / increment waiting
/ up(customers) / wake up barber
/ up(mutex) / release waiting
/ down(barbers) / block for 0 barbers
/ get_haircut() else
up(mutex) / shop full .. leave /
73Readers and Writers
- typedef int semaphoresemaphore mutex
1semaphore db 1int rc 0 / of
reading processes / - void reader(void) while(TRUE)
down(mutex) / exclusive access to rc /
rc rc 1 if(rc 1) down(db) / first
reader.. ? / up(mutex) / release rc
/ read_data_base() down(mutex) /
exclusive access to rc / rc rc - 1
if(rc 0) up(db) / last reader ? /
up(mutex) / release rc / void
writer(void) while(TRUE) down(db) /
get exclusive access / write_data_base()
up(db)
74Readers and Writers
- No reader is kept waiting, unless a writer has
already obtained the db semaphore - a second version of the readers-writers problem
requests that no writer is kept waiting once it
is ready - when a writer is waiting, no new
reader can start reading - In both cases processes may starve - writers in
the first version and readers in the second
version...
75Improve writers priority...
- typedef int semaphoresemaphore mutex 1,
mutex1 1semaphore db 1, rdb 1int rc
0, wc 0 / count readers and writers / - void reader(void) void writer(void)
while(TRUE) while(TRUE) down(rdb)
down(mutex1) - down(mutex) wc wc
1 rc rc 1
if(wc 1) down(rdb) if(rc 1)
down(db) up(mutex1)
up(mutex) down(db)
up(rdb) write_data_base() - read_data_base() up(db)
down(mutex) down(mutex1) rc rc -
1 wc wc -1 if(rc 0) up(db) if(wc
0) up(rdb) up(mutex)
up(mutex1)
76Readers-writers with Monitors
- Monitor reader_writer
- int numberOfReaders 0
- boolean busy FALSE
- condition okToRead, okToWrite
- public
- startRead
- if(busy (okToRead.queue)) okToRead.wait
- numberOfReaders numberOfReaders 1
- okToRead.signal
-
- finishRead
- numberOfReaders numberOfReaders - 1
- if(numberOfReaders 0) okToWrite.signal
-
77Readers-writers with Monitors (II)
- startWrite
- if((numberOfReaders ! 0) busy)
okToWrite.wait - busy TRUE
-
- finishWrite
- busy FALSE
- if(okToWrite.queue)
- okToWrite.signal
- else
- okToRead.signal
-
78Passing through a one-way Tunnel
- One-way tunnel enables any number of processes
in the same direction - a process in the right direction cannot enter
if there is at least one process in the left
direction inside the tunnel - Similar to Readers-Writers
- a solution must count2 the number of processes
inside the tunnel (in each direction) - a semaphore should be used for use of the
tunnel, call it busy - waiting processes block on another semaphore
waiting2 - two functions (that use a semaphore for mutual
exclusion) arrive(int direction) and leave(int
direction) are performed by entering and leaving
processes
79One-way tunnel - solution
- int count2
- semaphore mutex 1, busy 1
- semaphore waiting2 1,1
- void arrive(int direction) void leave(int
direction) - down(waitingdirection) down(mutex)
- down(mutex) countdirection - 1
- countdirection 1
if(countdirection 0) - if(countdirection 1) up(busy)
- up(mutex) up(mutex)
- down(busy)
- else up(mutex)
- up(waitingdirection)
80Deadlocks
- Deadlock of Resource Allocation
- Process A requests and gets Laser Printer
- Process B requests and gets Fast Modem
- Process A requests Fast Modem and blocks
- Process B requests Laser Printer and blocks
- Deadlock situation Neither process can move and
no process can release its allocated device
(Resource) - Comment both of the above resources (devices)
have exclusive access
81Resources
- Resources - Tapes, Disks, Printers, Database
Records, etc. - Some resources are non-preemptable (i.e. printer)
- Preemptable resources are easy (main memory)
- Resource allocation procedure
- Request
- Use
- Release only at the end and leave
- Block process while waiting for Resources
Iterate
82Defining Deadlocks
- A set of processes is deadlocked if each process
is waiting for an event that can only be caused
by another process in the set. - Necessary conditions for deadlock
- 1. Mutual exclusion resource used by only one
process - 2. Hold and wait process can request resource
while holding another resource - 3. No preemption only process can release
resource - 4. Circular wait 2 or more processes waiting for
resources held by other (waiting) processes
83Modelling deadlocks
- modelled by a directed graph (resource graph)
- Requests and assignments as directed edges
- Processes and Resources as vertices
- Cycle in graph means deadlock
F
P
A
S
R
Q
B
M
84the occurance of deadlocks
85A Resource Allocation Graph
86Resource Allocation Graph With A Deadlock
87A Cycle But No Deadlock
88Basic Facts
- If graph contains no cycles ? no deadlock.
- If graph contains a cycle ?
- if only one instance per resource type, then
deadlock. - if several instances per resource type,
possibility of deadlock.
89Dealing with Deadlocks
- Possible Strategies
- Prevention
- structurally negate one of the four necessary
conditions - Avoidance
- allocate resources carefully, so as to avoid
deadlocks - Detection and recovery
- Do nothing (Ostrich algorithm)
- deadlocks are rare and hard to tackle... do
nothing - Unix - process table with 1000 entries and 100
processes each requesting 20 FORK calls...
deadlock - users prefer a rare deadlock on frequent refusal
of FORK
90Deadlock prevention
- Attack one of the 4 necessary conditions
- 1. Mutual exclusion
- Minimize exclusive allocation of devices
- Use spooling (not good for all devices - Tapes
Process Tables) may fill up spools (disk space
deadlock)... - 2. Hold and Wait
- Request all resources immediately (before
execution) - Problem not known initially, inefficient
- or
- to get a new resource, free everything, then
request everything again (including new resource)
91...Attack one of the 4 necessary conditions
- 3. No preemption
- causes incorrect execution...
- ... without the process knowing it
- 4. Circular wait condition
- Allow holding only single resource (bad idea)
- Number resources, allow requests only in
ascending order - Request only resources numbered higher than
anything currently held - Not a workable solution - may have no solution
92Deadlock Avoidance
- System grants resources only if it is safe
- basic assumption maximal request per process is
known - Example 2 processes and 2 devices (Printer
Plotter)
93Bankers Algorithm (single resource)
- Simulate allocation of resources
- Bankers Algorithm (Dijkstra 1965)1. Pick a
process that can terminate after fulfilling the
request (enough free resources)2. Free all its
resources(simulation)3. Mark process
terminated4. If all processes marked, report
safe, halt5. If no process can terminate,
report unsafe, halt6. Go to step 1
94Safe and Unsafe states
- Safe stated
- Not deadlocked
- There is a way to satisfy all possible future
requests
(a)
(b)
(c)
- Fig. 6-9. Three resource allocation states (a)
Safe. (b) Safe. (c) Unsafe.
95deadlock detection - 4 resource-types
- Resources - (Tape-drives Modems Printers
CD-ROMs) - Existing resources - E (4 2 3 1)
- Available resources - A (2 1 0 0)
- Current allocation
- Request matrix
0
0
0
1
0
0
1
2
0
1
2
0
2
1
0
0
1
1
0
0
2
1
0
0
96Safety of states with multiple resources
- Granting a printer to B leads to a safe state
- Now, granting the last printer to E leads to
deadlock
97Multiple resources of each kind
- Assume n processes and m resource classes
- Use four arrays
- Current allocation matrix Cn x m
- Request matrix Rn x m
- Existing resources vector Em
- Available resources vector Am
- Detect deadlocks by comparing vectors
- for 1 j m Cij Aj Ej
- A B if for 0 i m Ai
Bi
98Bankers Algorithm (multiple resources)
- Look for a row R whose unmet resource needs are
all smaller then or equal to A. If no such row
exists, the system will eventually deadlock. - Assume the process of the row chosen finishes
(which is possible). Mark that process as
terminated and add all its resources to the A
vector - Repeat steps 1 and 2 until either all processes
are marked terminated, which means safe, or until
a deadlock occurs, which means unsafe.
99Checking safety - example
- Process Allocation Max Available
- A B C A B C A B C
- P0 0 1 0 7 5 3 3
3 2 - P1 2 0 0 3 2 2
- P2 3 0 2 9 0 2
E - P3 2 1 1 2 2 2
- P4 0 0 2 4 3 3 10
5 7 - The Need matrix will be
- P0 7 4 3 ? ltP1 P3 P4 P2 P0 gt is
Safe - P1 1 2 2
- P2 6 0 0
- P3 0 1 1
- P4 4 3 1
100one-more-request - example
- Suppose P1 requests now (1, 0, 2) - Request lt
Available - To execute the safety algorithm we note that if
granted we will have now for P1 - Allocation 3 0 2 Needed 0 2 0
- and a safe path ltP1 P3 P4 P0 P2 gt
- However, a request for 0 2 0 by P0 is not
safe - it is available
- But, none of the Needed tuples in the resulting
state is smaller than the Available tuple
101Deadlock Avoidance is not practical
- Maximum resource request per process is not known
initially - moreover, its point in time is also unknown
- Resources may disappear
- some devices leave the available pool (break
down) - New processes may appear
- the system is dynamic and processes are born and
die at any moment
102Deadlock Detection and Recovery
- Find if a deadlock exists
- if there is, which processes and resources
- Detection detect cycles in resource graph
- Algorithm DFS node and arc marking
103Find cycles
- For each node, N, in the graph, perform the
following 5 steps with N as starting node - 1. Initialize L to the empty list and designate
all arcs as unmarked - 2. Add the current node to the end of L and check
if the node appears twice in L. If it does, the
graph contains a cycle, terminate. - 3. If there are any unmarked arcs from the given
node, go to 4., if not go to 5. - 4. Pick any unmarked outgoing arc and mark it.
Follow it to the new current node and go to 2. - 5. We have reached a deadend. Go back to the
previous node, make it the current node and go to
2. If the node is the initial node, there are no
cycles in the graph, terminate
104Detection - extract a cycle
- 1. Process A holds R and requests S
- 2. Process B holds nothing and requests T
- 3. Process C holds nothing and requests S
- 4. Process D holds U and requests S and T
- 5. Process E holds T and requests V
- 6. Process F holds W and requests S
- 7. Process G holds V and requests U
105When should the system check for deadlock ?
- For every request instance - too expensive
- every k minutes...
- whenever cpu utilization drops strongly (good
sign of deadlock..)At least two processes in
H,W
106Recovery
- Preemption - possible in some rare cases
- temporarily take a resource away from its current
owner - Rollback - possible with checkpointing
- keep former states of processes (checkpoints) to
enable release of resources and going back - Killing a process - easy way out, may cause
problems in some cases, depending on process
being rerunable - Bottom line hard to recover from deadlock,
avoid it
107Example - deadlocks in DBMSs
- For database records that need locking first and
then updating - Deadlocks occur frequently because records are
dynamically requested by competing processes - DBMSs, therefore, need to employ deadlock
detection and recovery procedures - Recovery is possible - transactions are
checkpointed - release everything and restart - Not useful for network messages sent and
received, for example - cannot be terminated and
started over safely
108Factors Determining Process to Kill
- What the priority of the process is
- How long the process has computed, and how much
longer the program will compute before completing
its designated task - How many and what type of resources the process
has used (for example, whether the resources are
simple or preempt) - How many more resources the process needs in
order to complete - How many processes will need to be terminated
- Whether the process is interactive or batch
109Deadlock handling combined approach
- allocation (and deadlocks) are all around -
different strategies can be used for different
classes of resources - Simplistic example - four classes
- Internal resources used by the system PCB
- Central memory for users processes
- Assignable devices Tape drives, etc.
- Swap space on disk for user processes
110Combined approach (cntd.)
- Internal Resources use Prevention through
resource ordering, no selection among pending
processes - Central memory use prevention through
Preemption, a process can always be swapped-out - Assignable devices if device-requirement
information is available, use Avoidance - Swap space use Preallocation, since maximal
storage requirements are known in advance
111Additional issues of deadlock
- Deadlocks may occur with respect to actions of
processes, not resources - waiting for semaphores - Starvation can result from a bad allocation
policy (such as smallest-file-first, for
printing) and for the starved process will be
equivalent to a deadlock (cannot finish running) - Summary of deadlock treatment
- Ignore problem
- Detect and recover
- Avoid (be only in safe states)
- Prevent by using an allocation policy or
conditions
112The Situation in Practice
- Most OSs in use, and especially NT, Solaris ,
ignore deadlock or do not detect it - Tools to kill processes but usually without loss
of data - In Windows NT there is system call
WaitForMultipleObjects that request all resources
at once - System provides all resources, if free
- There is no lock of resources if only few are
free - Prevents Hold Wait, but difficult to implement!