Topic: Deadlock (Chapter 7 in textbook: Operating System Concepts) Announcements: Midterm 1 will be coming soon. - Until now you have heard about processes. From now on you'll hear about resources, the things operated upon (i.e. used) by processes. Resources range from cpu time to disk space to channel I/O time. - Resources fall into two classes: - Preemptible: processor, I/O channel, main memory. Can take resource away, use it for something else, then give it back later. - Non-preemptible: once given, it can't be reused until process gives it back. Examples are file space, terminal, printer, semaphores. - This distinction is a little arbitrary, since anything is preemptible if it can be saved and restored. It generally measures the difficulty of preemption. - OS makes two related kinds of decisions about resources: - Allocation: who gets & keeps what, for non-preemptable resources. Given a set of requests for resources, which processes should be given which resources in order to make most efficient use of the resources? - Scheduling: - for preemptable resources: how long can they keep it. When more resources are requested than can be granted immediately, in which order should they be serviced? Examples are processor scheduling (one processor, many processes), memory scheduling in virtual memory systems. - System typically has resources. - Process loop is typically: (a) request resource, (b) use resource, (c) release resource. - Deadlock: Analogy to Traffic Jam: each lane is waiting for another lane to clear (see figure below). = = = __ = = = = \/ = = = = __ = ===== ================ \/ ==== ||> ||> ||> ||> ||> __ ===== ================ \/ ==== = /\ = = __ = = "" = = \/ = = /\ = = __ = = "" = = \/ = ==== /\ ================ ==== "" <|| <|| <|| <|| <|| ==== /\ ================ ==== = "" = = = - Deadlock example: semaphores. - Two processes: one does P(x) followed by P(y), the other does the reverse. - In this case, the resources are the semaphores X and Y. This figure can explain it: P1 P2 P(X) P(Y) P(Y) P(X) - Deadlock is one area where there is a strong theory, but it's almost completely ignored in practice. Reason: solutions are expensive and/or require predicting the future. - Deadlock: a situation where each of a set of processes is waiting for something from other processes in the set. Since all are waiting, none can provide any of the things being waited for. - This is relatively simple minded. We could have: (a) processes waiting for resources of the same type - e.g. there are 4 tape drives. (b) More than 2 processes. E.g. A has x, B has y, C has z, A needs y, B needs z, and C needs x. (Note the circularity of the graph) |X| ----> A ----> |Y| ----> B ---->|Z| ----> C-----| /\ | |-------------------------------------------------| (c) could be waiting for more pieces of a resource - e.g. each needs a certain amount more memory, and holds memory in the mean time. - In general, there are four conditions for deadlock: (We consider only situations with a single example of each resource) - Mutual exclusion: two or more resources cannot be shared. The requesting process has to wait. E.g. tape drives, printers. - Hold and wait - the process making a request holds the resources that it has, and waits for the new ones. This also implies multiple independent requests: processes don't ask for resources all at once. - No preemption. Once allocated, a resource cannot be taken away. - There is a circularity in the graph of who has what and who wants what. Let Ri be a resource. The graph has an arrow from Ri to Rj if there is a process holding Ri and requesting Rj. (Can call this a hold and wait graph or resource request graph.) - (This condition actually implies the hold and wait condition.) - Approaches to the deadlock problem fall into two general categories: - Prevention: organize the system so that it is impossible for deadlock ever to occur. May lead to less efficient resource utilization in order to guarantee no deadlocks. - Cure: determine when the system is deadlocked and then take drastic action. Requires termination of one or more processes in order to release their resources. Usually this isn't practical. [cure means fixes the deadlock problem.] - Deadlock prevention: must find a way to eliminate one of the four necessary conditions for deadlock: - Create enough resources so that there's always plenty for all - It may not be possible to make enough of some resources, e.g. printer, tape drive, etc. - Make all resources shareable. - Some resources are not shareable - e.g. printer, tape drive. - Don't permit mutual exclusion: not feasible - Virtualize non-shared resources (e.g. printer, card reader - spooling) Note: spooling refers to putting jobs in a buffer, a special area in memory or on a disk where a device can access them when it is ready. Spooling is useful because devices access data at different rates. The buffer provides a waiting station where data can rest while the slower device catches up. - Use only uniprogramming - Don't allow waiting and holding. - Process must crash if resource not immediately available. - Phone company does this - if your call doesn't go through, it is dropped. - Process must request everything at once. (requires sufficient knowledge - is this feasible?) - Probably will end up requesting too much, just to be safe. E.g. too much memory, or both input and output devices all at once, even though they are not used at same time. - Starvation is possible - may wait indefinitely long until everything you need is free. - May not be possible - e.g. system has 2 tape drives. Need 2 for input and 2 for output. Must complete input and then request output drives. Can't ask for all at once. - Process must release all current resources before requesting any new ones. (Some resources may not be releasable and re-acquirable, without restarting the process - e.g. printer, tape drive. Sometimes, process may not be restartable - e.g. you have modified a database record - e.g. balance in checking account.) - Allow preemption. - Some things can be preempted - e.g. memory, CPU (registers can be saved and restored), disk. - Make ordered or hierarchical requests. E.g. ask for all R1's, then all R2's, etc. All processes must follow the same ordering scheme. In that case, for there to be an arrow from Ri to Rj, j>i; i.e. there can't be a circular wait. We can call this the resource request graph. Of course, for this you have to know in advance what is needed. - Illustration of Deadlock Occurance - see figure below ^ |_ _____________ ^ | | | | | | | D ^ | |_ | |______ | R1 ^ | | | | | | | | C | | v | |_ |______e<-deadlock | | R2 | | | | | | B | | | v |_ |_____________| | | A P2 | t1 t2 t3 t4 |______|______|______|______|______> <-----R1----> <-----R2----> P1--------------------> Explanation of Figure 1: The x-axis represents the computational progress of process 1 (P1) The y-axis represents the computational progress of process 2 (P2) R1 and R2 are resources which are not shareable The times during which P1 and P2 use R1 and R2 are shown by the ranges around R1 and R2. For example, P1 uses R1 from t1 to t3. The graph is divided into four regions A, B, C, and D: A - The region in which normal computation is occurring. This region includes everything which is not in B, C, or D. B - This region is interesting because P1 is using R1 and will eventually request R2. But, P2 is using R2 and will eventually request R1. The two processes will wait for the resource that the other process is using and it is inevitable that point e will be reached. The two processes are in deadlock at point e. Computational progress can only move forward so from anywhere in B it is guaranteed that point e and hence deadlock will be reached. C - Impossible! Both processes cannot be using both resources at the same time. This region is not legal. D - This region is also impossible. "You can't get there from here!" This is a legal region, but to get there would require decomputation because C is not a legal region. Decomputing is not considered in this course. Bottomline: If you want P1 and P2 to compute and do their thing, they need to stay in region A. Deadlock Avoidance - We can "avoid" deadlock, if we only make resource allocations that are "safe." - A safe state is one in which deadlock is not inevitable. I.e. one in which there exists a sequence of tasks, such that there are sufficient resources to complete one task, and after the resources released by that process are reclaimed, the state is still safe. (I.e. there exists a sequence of task completions which leads to all tasks being completed.) Any tasks left after trying this are deadlocked. After a task completion a safe state enters another safe state. - A safe allocation is one that leads to a safe state. - A safe sequence is the sequence of task executions leading from a safe state to another safe state. A complete safe sequence leads to all tasks being completed. - If the state is not safe, it is unsafe. Unsafe states lead to deadlocks. (Exception - a task may release some resources temporarily, which may enable other tasks to complete.) - Banker's Algorithm - The banker's algorithm is the algorithm used to compute a safe sequence. - It is called the banker's algorithm, because if the processes are borrowers, the resource is money, and customers come in with partial loan requests, then the algorithm can be used to compute whether the bank can make the necessary loans to allow the customers to repay. - Consider the case of construction loans. - Do example: process has max need A 90 100 B 50 110 C 30 160 and bank has $20 available. - For every process J (j=1...n), we need to know - Max(j,k), which is the maximum number of k that will be requested by j. - Allocation(j,k), which is the number currently allocated. - Need(j,k), which is the number still needed (Need(j,k) = Max(j,k) - Allocation(j,k)). - For every resource k, we need to know Available(k), k=1...m. (available(k) is the number still available) - Algorithm works as follows: (a) Given a request, let Alloc*(j,k) and Avail*(k) be the state after the request is granted. (b) If all processes can finish with no additional resources, then no deadlock; i.e. state safe. (c) Find a process x, for which for all k, Need*(x,k)<=Avail*(k). If there is no such process, then deadlock; i.e. state unsafe. Otherwise, "mark" process finished. (d) If all processes are marked finished, or can finish with no additional resources, then no deadlock; i.e. state safe. (e) Let for all k, Avail*(k) = Avail*(k) - Alloc*(x,k). Goto (c). - Note that if there is only one resource (money), then the banker's algorithm applies to loans. Q: Is it easy to compute max(j,k)? A: Often don't know how much "k" needed. - Feasibility? - Banker's algorithm is only possible if we have knowledge of maximum resource needs in advance. - Overhead of running Banker's Algorithm if there are lots of processes and resources may be significant - not efficient. - Recovery From Deadlock - We can either prevent deadlocks, or recover from them. Given that we often do not know what resources will be needed in the future, it will be impossible to prevent them. - General idea is to periodically run a deadlock detection algorithm. It looks for circuits in the resource allocation graph. If it finds a circuit, must break it. - Do this periodically, - or when system seems slow, - and/or when there are a bunch of processes that seem to have waited a long time, - and/or when there are lots of processes, and also lots of CPU idle. - Two general approaches: - Kill all deadlocked processes. - Kill one process at a time until the deadlock cycle is eliminated. (This is preferable - less damage, and less lost computation.) - Want to select minimum cost processes to kill, which depends on whether the process can be restarted at all, and if so, how much computation has been lost. - Rollback - causing the process to return to an earlier state. If the process goes back to the beginning (restart), it is a total rollback. In some cases, (especially if checkpoints have been kept), may be able to do partial rollback to break a deadlock. - Checkpoint - a copy of the state of the process at some past time. [Generally, people use checkpoint for system failure.] - In general, prevention of deadlock is expensive and/or inefficient. Detection is also expensive and recovery is seldom possible (what if process has things in a weird state?). - IBM OS/360 solutions: - Data set names are enqueued all at once on a job basis. - Devices, volumes, and memory are allocated in order on a per-job-step basis. - Temporary file space can cause deadlocks. (or requests for additional file extents). Operator intervenes and kills a job. - Multics Solution: in main path of process, must request resources in specific order. In secondary paths (which can be preempted), must release resources held out of order (i.e. need "wait permit"). - MTS (University of Michigan Timesharing System) - put in only mild constraints on resource allocation. Run deadlock detection algorithm periodically, and kill jobs that are found to be deadlocked. Seldom found to be the case. - Unix - synchronization: - Synchronized use of a resource done with locked flag and a wanted flag. - When a kernel process wants a resource, it checks the locked flag. If not in use, sets the locked flag, and uses resource. (Must obviously set mutex to do this atomically.) - If locked flag is set, sets wanted flag, and calls sleep(), with a wait channel associated with that resource (typically the data structure used to describe the resource.) - When done, clears locked flag, and if wanted flag is set, calls wakeup() to awaken all of the processes that called sleep() to await access to the resource. Those processes then compete to get lock. - Note that this scheme is equivalent to the wake-up waiting flag scheme described in Watson's book. It fails if resource is unlocked between time lock is checked and flag is set. - Some processes that cannot be made to sleep (in "bottom half" of Unix kernel). Those processes are prevented from trying to use a locked resource (any resource that is shared between top and bottom of kernel) by raising the priority of the portion of the kernel that is running to prevent the other portion of the kernel from running. - Unix - Deadlock - Normally, (kernel?) process which is holding resources and finds that it wants a locked resource must release all of the resources it is holding before it goes to sleep. - In special cases (e.g. going down a directory tree), resources are accessed in order. In that case, there can be no circuit in the resource alloc. graph, and so no sleep occurs. - Unix - File Locking - Only one process may have advisory exclusive lock or many processes may have advisory shared lock. Lock requests block if the lock is not available. Because locks are advisory, process may ignore them if desired. - Conditional lock request is possible - i.e. get error return if lock is set, rather than blocking. - Deadlock checking in Unix for file locks is limited. System checks to make sure it doesn't deadlock with itself (i.e. it already holds an exclusive lock). Any application complicated enough to require deadlock detection is required to develop its own.