1. Deadlock1.1 Basic concepts
Deadlock: A deadlock refers to a state in which multiple threads (processes) executing concurrently in the system are permanently blocked because they cannot obtain the required resources.
The necessary conditions for deadlock generation:
A. Exclusive exclusion: Refers to a resource that can be used by only one task (thread or process) at any time. If there are other tasks requesting the resource at this time, the requestor can wait until the resource-owning task frees the resource.
B. Non-preemption: When a task has a resource, it cannot lose ownership of the resource unless it voluntarily releases it.
C. Hold and wait: means that the task already has at least one resource, and then waits for other resources to be available.
D. Cyclic wait: When a deadlock occurs, there must be a task-a circular chain of resources, that is, the P0 in the task set {p0,p1,p2,,pn} is waiting for a P1 to occupy the resources, P1 is waiting for the resources that are occupied by P2, ..., and Pn is waiting for resources that have been consumed by P0.
These 4 prerequisites must be fulfilled when a deadlock is generated, so that the processing of deadlocks is to avoid, prevent these 4 conditions from being set up at the same time, or destroy any one of these conditions when 4 conditions are set up simultaneously. Methods for handling deadlocks include:
- Prevent deadlocks
- Avoid deadlocks
- Deadlock detection
- Unlock deadlock
1.2 Preventing deadlocks
This approach is to prevent deadlocks by setting certain constraints on resource requests to destroy one or more of the four necessary conditions that produce deadlocks. The prevention of deadlocks is a more easily implemented method and has been widely used. However, due to the often restrictive conditions imposed, the system resource utilization and system throughput can be reduced.
The restrictions used generally include:
A. Elimination of holding and waiting for this condition: The task must apply for all of its required resources at once, and it can continue to run only if all the resources are available. Since the task is requested to complete all of its resources at one time, there is no holding and waiting for this situation. But the lack of it is that it is difficult to predict a task needs those resources in a real system, and even if the resources needed for the task can be predicted, the task does not necessarily need to use these resources at run time, because the resources that are really needed in the run are determined by the code path. This could result in the possession of unused resources leading to waste, and the more serious problem is that the use of this precaution implies a requirement that the resources required for the task must be released at the same time. This means that all resources can only be released when the task is no longer required, which can result in significant resource wastage.
B. Eliminate this condition: If a request for a new resource cannot be fulfilled, it should release the resources it has already held. When the task attempts to request resources again, it should apply for resources that have been previously held and resources that are new to them. In contrast to the first condition, this condition does not require the task to apply for all of the resources it needs at one time, but to apply as needed. However, the method also has serious shortcomings, so it will release all the resources that have been held after the application of a resource failure, when the request for resources to re-try to apply all the resources required at this time, this need to track the required resources, and when trying again, may have been previously held resources have become unusable, This undoubtedly increases the complexity of programming.
C. Eliminate the loop wait for this condition: The method requires a uniform number of resources, and if the task already holds a resource numbered I, it can only request a resource with a number greater than I. This eliminates the loop waiting for this condition.
1.3 Avoiding Deadlocks
This method is also a pre-prevention strategy, but it does not have to take a variety of restrictive measures to destroy the four necessary conditions for deadlocks, but in the dynamic allocation process of resources, in a way to prevent the system from entering the unsafe state, so as to avoid the deadlock. The most representative algorithm for avoiding deadlock algorithm is the banker algorithm proposed by Dijkstra E.W in 1968: Banker algorithm is one of the most representative algorithms to avoid deadlock. In the avoidance of deadlock method allows the task to request resources dynamically, but before the system allocates resources, the allocation of resources should be calculated before the security, if the allocation will not cause the system to enter an unsafe state, then allocate, otherwise wait. A security sequence is a task sequence {P1,...,PN} is secure, that is, for each task Pi (1≤i≤n), the amount of resources required for the task after it does not exceed the current amount of resources currently occupied by the system and all processes PJ (J < i). Security state and unsafe state:
- Security state: If there is a security sequence P1,...,PN that consists of all processes in the system, the system is in a secure state. The security state must be no deadlock occurs.
- Unsafe state: There is no security sequence. Unsafe states do not necessarily cause deadlocks.
To use this strategy, each task needs to estimate the maximum amount of resources it needs, and then the system uses that information to establish a resource requirements table for use. But it is usually difficult to predict. And because it is estimated that the maximum amount of resources, while the actual running of a task does not necessarily actually use the maximum amount of resources that it estimates, so using this estimate for deadlock avoidance can lead to unnecessary blocking.
1.4. Detect Deadlocks
Deadlock detection is not required to take any restrictive measures beforehand, it is used only to deadlock during operation. and accurately determine the tasks and resources associated with deadlocks, and then take appropriate steps to remove the deadlocks that have occurred from the system.
1.4.1 Deadlock Detection for single-instance resources
The so-called single-instance resource is only 1 of that resource, so it can only be used for one task.
- Draw out the use of system resources for deadlock detection, and apply to graph graph. Where a task currently has a resource, draw a forward edge from that resource to the task, or if the task is requesting a resource, draw a forward edge from the task to that resource.
- Take a node that has not been traversed from the graph, mark the node in graph graph as traversed, and create an empty list of nodes that hold graph graph.
- Check that the node already exists in the list L, and if so, there is a deadlock; otherwise add the node to the tail of the list L
- Check to see if there are any non-checked forward edges from this node in graph graph, and if not, skip to step 6
- Select one of these forward edges, mark the directed edge as checked in graph graph, treat the inbound node as the current node, mark the current node as traversed in graph graph, and jump to step 3
- If L is not empty, the last node in L is removed from L, and if L is not empty, the last node in L is set to the current node and jumps to step 4
- If there are no traversed nodes in graph graph, jump to step 2; otherwise the algorithm ends, there is no deadlock
1.4. Deadlock Detection for 2 multi-instance resources
A multi-instance resource is a resource that can be used by more than one task at the same time
- Lists the amount remaining for each resource and the amount that each task needs to use for deadlock detection
- Find a task that requires a quantity of each resource less than or equal to the amount of the corresponding resource remaining at the time of the system, i.e. its request can be satisfied; If no such task is found, the system has a deadlock
- Mark Step 2 identifies the task to be operational and adds the amount of resources it holds at this time to the amount of the system's remaining resources
- If there is a task in the system that is not marked as operational, go back to step 2; otherwise, the system does not have deadlock
1.5 Unlocking the deadlock
It is generally used in conjunction with detection deadlocks. A common practice is to undo or suspend tasks so that you can reclaim some resources, and then assign those resources to a blocked task and turn it into a ready state to continue running. Deadlock detection and lifting measures, it is possible to make the system to achieve better resource utilization and throughput, but the implementation of the most difficult.
These policies are generally implemented and supported by the system, and there are some principles to be consulted in the application writing:
A. Do not hold locks in long-duration operations, such as I/O, that may adversely affect performance.
B. Do not hold locks outside the calling module and may re-enter the function of the module.
C. In general, a coarse-grained lock is preferred, and fine-grained locks are used if a coarse-grained lock is determined to have a significant impact on performance.
D. When using multiple locks, try to lock all tasks in the same order to avoid deadlocks.
2. Priority Reversal2.1 Basic Concepts
Priority reversal: Priority reversal refers to a state in which a high-priority task is blocked by waiting for resources held by a low-priority task, while a low-priority priority is in a preemptive system. Its basic performance has two kinds of situations:
A. Low priority T1 task holding resources R run; High priority tasks T2 start running, high priority tasks need to use resource R, but cannot get the resource, and thus blocked; low-priority tasks T1 continue to run and appear as if high-priority tasks are preempted by low-priority tasks
B. Low priority T1 task holding resources R run, high priority task T2 start to run, high priority tasks need to use resource R, but cannot get the resource, thus blocking, low priority task T1 is scheduled to run, when a priority is less than T2 but greater than T1 task T3 started, it will preempt task T1; it looks like the task T3 preemption High-priority task T2, if the situation 1 because of competitive resources is more easily seen, the situation 2 is more confusing, T2 and T3 there is no competition between, T3 actually run before T1.
In most cases, priority reversal does not cause problems, because high-priority tasks are only deferred, but sometimes this can be a problem.
2.2 Workaround
Because of the priority reversal and the shared use of resources, you can solve this problem by adding an access control protocol to the resource, you can solve the priority reversal problem by using the following three resource access control protocols:
A. Priority Inheritance Protocol
B. Ceiling-Priority Protocol
C. Priority Ceiling Protocol
2.2.1 Priority Inheritance Protocol
Access control rules for the Protocol: If a task T1 is blocked because it cannot get the resource R, and its priority is higher than the T2 of the resource R's holder, the priority of its holder T2 to the task T1 is increased. Specific rules:
- If resource R is not available, the task T that requested the resource is blocked
- If resource R is available, assign it to the requester task T
- When a task that has a priority higher than task T requests resource R, it increases the priority of T to the priority of the high-priority task
- When task T releases resource R, its priority is restored
2.2.2 Ceiling-Priority Protocol
In the ceiling priority, all the resources required for all tasks are known and the priority of all tasks is known. Each resource has a ceiling priority, which equals the highest priority for all tasks that need to use the resource. For example, the system has a task T1 (priority 4), T2 (priority 6), T3 (priority 8), T4 (priority 10), resource R1, R2, R3, R4, where the task T1 need to use resources R1, R3, task T2 need to use resource R2, task T3 need to use resources R2, R4, Task T4 requires the use of resources R1, R3, R4. The ceiling priority for resources R1, R2, R3, and R4 are: 10,8,10,10. The rules for using this protocol are:
- If resource R is not available, the task T that requested the resource is blocked
- If the resource R is available, it is assigned to the requester task T, and the priority of task T is promoted to the resource's ceiling priority
- When a task T releases a resource with the highest ceiling priority, it resizes its priority to ensure that the task runs at the highest ceiling priority in its own resource at any time
- When a task releases all the resources it uses, its priority goes back to the priority assigned to it by the system
2.2.3-Priority Ceiling Protocol
Similar to the ceiling priority protocol, all the resources required for all tasks in the priority ceiling protocol are known and the priority of all tasks is known. Each resource has a ceiling priority, which equals the highest priority for all tasks that need to use the resource. For example, the system has a task T1 (priority 4), T2 (priority 6), T3 (priority 8), T4 (priority 10), resource R1, R2, R3, R4, where the task T1 need to use resources R1, R3, task T2 need to use resource R2, task T3 need to use resources R2, R4, Task T4 requires the use of resources R1, R3, R4. The ceiling priority for resources R1, R2, R3, and R4 are: 10,8,10,10. The difference is in the rule that there is a current priority ceiling in the priority ceiling protocol, and that the current priority ceiling is equal to the highest ceiling priority of all resources currently being used, based on the current priority ceiling, the rules of the Protocol are:
- If resource R is not available, the task T that requested the resource is blocked
- If resource R is available and task T takes precedence over the current priority ceiling, resource R is assigned to T, otherwise see rule 3
- If resource R is available and the ceiling priority of a resource held by task T equals the current priority ceiling, resource R is assigned to T, otherwise the task T will block
- If the priority of a task T is higher than the priority of the task T1 that blocked it, T1 will inherit the task T and run with that priority task, and T1 will revert to its original priority after it releases all resources with a priority higher than t.
The protocol combines the priority inheritance protocol and the ceiling priority protocol to compute a current ceiling priority with the priority ceiling of the resource currently being used, but it differs from the priority of the running task, and the priority of the task is changed only when the task blocks a high-priority task. And this change is not simply set as the current priority change board, but instead inherits the priority of the high priority task it blocks
3. Ensure that the program has only one running instance
One requirement that is often seen is that only one running instance of the application is running. This can be achieved by recording locks, which are implemented by:
A. Create a special file after launching the app, the location and name of the file is specific
B. Try a record lock on the file and lock the entire file
C. If the lock fails, it indicates that an application instance is running, exiting
D. Continuation of the implementation
The reason that a record lock can be achieved is because: depending on the nature of the record lock, regardless of how the holder process of the record lock ends, the system automatically releases all record locks it holds-if the process does not release it itself. Thus, as long as it cannot be locked, it must indicate that a running instance is holding the lock.
Named semaphores can also be used for this purpose because the named semaphore also has a similar feature (the system automatically shuts down all named semaphores it opens when the process terminates).
Synchronization and Mutex