Deadlock and Starvation Deadlock: A set of Processes holding resources and blocked waiting for others held by a processes in the same wait set. Starvation: Resource is always granted to other waiting processes Example: 2 tape drives, P1 and P2 each hold one and need both Example: P0: wait(A); wait(B); P1: wait(B); wait(A); Resource: The narrow bridge Deadlock Resolution: Cars back up (preemption and rollback) Starvation is possible.
Conditions Necessary for Deadlock Hold and wait: Processes partially allocate resources Limited Resources: There is a limited number of resources of a particular type. Processes will block if requesting a limited that is unavailable. No preemption: A resource can be released only voluntarily by the process holding it. Circular wait: there exists a process set {P0, P1, …, P0} where P0 is waiting for a resource that is held by P1, P1 is waiting for a resource that is held by P2, …, Pn–1 is waiting for a resource that is held by P0. P0 P1 wait (A); wait(B) wait (B); wait(A)
Exercise Refer to the above figure How are all four conditions present? How can deadlock be prevented, avoided, and detected?
Directed Resource Allocation Graph Process Resource Type with 4 instances Pi requests Rj instance Pi holding Rj instance Graph: A set of vertices & edges. Two vertex categories: Circles: represent Processes {P1, P2, …, Pn} Rectangles: represent Resource types {R1,R2,…,Rm} Two edge categories Pi requests resource type Rj: PiRj Resource Rj instance assigned to Pi: RjPi Pi Pi Resource categories: CPU cycles, memory files, I/O devices Access: request, utilize, then release
Graph Cycles No cycles no deadlock. cycles Deadlock if there is only one instance per resource type Deadlock possibility several instances per resource type and all processes are waiting for a resource No Deadlock Deadlock
Possible Deadlock class B implements Runnable; { private Lock one, two; public(Lock one,Lock two) { this.one = one; this.two = two; } public void run() { try { two.lock(); doSomething(); one.lock(); doSomethingElse(); finally { two.unlock(); one.unlock(); } } } class A implements Runnable; { private Lock one, two; public(Lock one,Lock two) { this.one = one; this.two = two; } public void run() { try { one.lock(); doSomething(); two.lock(); doSomethingElse(); finally { two.unlock(); one.unlock(); } } } Deadlock possible depending on timing, but sometimes will not occur
Handling Deadlocks Prevention: Eliminate one of the four necessary conditions Avoidance: Ensure that the system will never enter a deadlock state. Recovery: Allow the system to enter a deadlock state and then recover. Ignore: Most operating systems, including UNIX and WINDOWS, pretend that deadlocks never occur. It is up to application programs to handle.
Deadlock Prevention Hold and Wait Limited Resources No Preemption Eliminate one of the necessary conditions Hold and Wait – sharable resources like read-only files are deadlock free Limited Resources Resources with unlimited instances are deadlock free. No Preemption Release all resources instead of blocking on a resource request. The preempted resources are added to the resource wait list. Restart when all requested resources are available. Circular Wait (Disadvantage: Low resource utilization) Impose a total ordering of resource types. Processes must request resources in an increasing order of enumeration. Processes cannot request resources when already holding others.
Deadlock Avoidance The system must have advance knowledge of total needed resources Processes will not hold a resource forever Safe state: A sequence of requests exist that can be satisfied without deadlock Safe state no deadlocks Unsafe state possibility of deadlock Avoidance disallows all unsafe states Algorithm Pseudo code System is initially safe At each request for a resource IF allocation causes an unsafe state THEN block the process ELSE grant the allocation At each resource release WHILE a blocked request can be granted safely Grant the allocation; unblock the process
Resource Allocation Graph for Deadlock Avoidance Dashed line: Pi to Rj Pi may request Rj Solid line: Pi to Rj Pi has requested Rj. Solid line: Rj to Pi: Rj is assigned to Pi. If resources with a single instance, implement a cycle detection algorithm (Recall Union Find algorithm from Data Structures)
Dijkstra Banker’s Algorithm Original algorithm’s purpose: Prevent runs on a bank Notation request[p, r] = k means process p wants k instances of resource type r available[r] = k means there are k resources of type r available need[p,r] = k means process p potentially needs k resources of type r allocated[p,r] = k means process p owns k resources of type r Pseudo code upon processor p request for resource r Throw Exception IF request[p,r[ > need[p,r] IF request[p,r] > available[r], block process p Tentatively allocate requested resources to process p: available[r] -= request[p,r]; allocated[p,r] += request[p,r]; need[p] -= request[p,r] IF system state is safe THEN allocation is okay ELSE restore original state and block process p
Safe State Algorithm Safety Algorithm Let work and finish be arrays of length R and P, respectively. Copy available to work FOR p = 1 to P, finish [p] = false WHILE a p exists such that for all r, finish [p]=false AND need[p, r] work[r] THEN for all r, work[r] += allocated[p, r] finish[i]=true IF finish[p]==true for all p, then safe system state (allocate resources) ELSE block process Data structures for this algorithm: P processes, R resources Available: available[r]=k k instances of resource r are available Max: max[p, r]=k Process p may request up to k of resource r Allocated: allocated[p, r]=k Process p owns k instances of resource r Need: need[p, r] = k Process p may need k more of resource r Note: need [p,r] = max[p, r] – allocation [p,r]
Deadlock Avoidance Example 5 processes P0 through P4 and 3 resources (A,B,C) Resource A has 10 instances Resource B has 5 instances Resource C has 7 instances. The system state: Safe < P1, P3, P4, P2, P0> satisfies the safety criteria. P1 now requests (1,0,2). Request Need Request Available Execute safety algorithm: <P1, P3, P4, P0, P2> satisfies the safety criteria Questions: Is P4 request for (3,3,0) safe? Is P2 request for (0,2,0) safe? Need = Max – Allocated A B C A B C A B C P0 7 4 3 = 7 5 3 – 0 1 0 P1 1 2 2 = 3 2 2 – 2 0 0 P2 6 0 0 = 9 0 2 – 3 0 2 P3 0 1 1 = 2 2 2 – 2 1 1 P4 4 3 1 = 4 3 3 – 0 0 2 Available = 3 3 2
Deadlock Detection Allow Deadlock, detect its occurrence, recover Design Issues When, and how often, to invoke the detection algorithm? Is it possible to detect the cause? How often is a deadlock is likely to occur? How many processes are part of the deadlock? Which group of processes should we abort? All involved in the deadlock? One at a time till the deadlock cycle is eliminated? Can we roll back to a safe state and try again? What is the same process is always the victim How many rollbacks should we allow? How to determine the cost of recovery? Process priority and type (interactive or batch) Process run time, and the estimated time to completion. Resources the process has used and those still needed
Deadlock Detection Single resource instances Single Instance Maintain wait graph Nodes are processes. Pi Pj if Pi is waiting for Pj Periodically invoke an algorithm to detect a cycle in the graph The text claims cycle detection requires O(|V|2). Is that true? Resource allocation wait-for graph graph
Deadlock Detection Multiple Resource Instance Algorithm Data structures for this algorithm: P processes, R resources Available: available[r]=k k instances of resource r are available Allocated: allocated[p, r]=k Process p owns k instances of resource r Request: request[p, r] = k Process p requests k instances of resource r Let work and finish be arrays of length R and P, respectively Copy available to work FOR all p and r // no circular wait if process doesn’t hold any resources IF allocated[p,r] 0, then finish[p] = false ELSE finish[p] = true // No circular wait for a process that can be fully satisfied WHILE p exists where for all r, finish[p] == false and request[p,r] work[r] SET work[p] += allocated[p,r] AND finish[p] = true IF finish[p] == false, for some p, 1 p P THEN the system and process p is in deadlocked
Deadlock Detection Example Description of the system state Five processes: P0 through P4 Three resource types A has 7 instances B has 2 instances C has 6 instances Allocated Request Available A B C A B C A B C P0 0 1 0 0 0 0 0 0 0 P1 2 0 0 2 0 2 P2 3 0 3 0 0 0 P3 2 1 1 1 0 0 P4 0 0 2 0 0 2 Sequence <P0, P2, P3, P1, P4> causes finish[p] = true for all p If P2 requests a type C resource, the request array becomes: A B C P0 0 0 0 P1 2 0 1 P2 0 0 1 P3 1 0 0 P4 0 0 2 We can reclaim P0 resources, but there are not sufficient resources to fulfill the other requests Deadlock involves P1, P2, P3, and P4
Conclusion Most current operating systems ignore deadlocks. It is left to the application developer to handle There is not one approach that is sufficient The eventual solution is to combine the three basic approaches of prevention, avoidance, and detection Different resource classes would employ a selected solution