Priority Inversion:优先级反转

来源:互联网 发布:淘宝我的账户怎么修改 编辑:程序博客网 时间:2024/04/30 20:49

 

Suppose an application has three threads:

Thread 1 has high priority

Thread 2 has medium priority

Thread 3 has low priority

Thread 1 and thread 2 are sleeping or blocked at the beginning of the example. Thread 3 then runs and enters a critical section.

At that moment, thread 2 begins running, preempting thread 3 because thread 2 has a higher priority. So, thread 3 continues to own a critical section.

Later, thread 1 begins running, preempting thread 2. Thread 1 tries to enter the same critical section that thread 3 owns, but because it is owned by another thread, thread 1 blocks, waiting for the critical section.

At that point, thread 2 would begin running because it has a higher priority than thread 3, and thread 1 is not running. Thread 3 would never release the critical section that thread 1 is waiting on because thread 2 would continue running.

So, the highest-priority thread in the system, thread 1, becomes blocked waiting for lower-priority threads to execute.

 

 

 

In computer science, priority inversion is a problematic scenario in scheduling when a high priority task is indirectly preempted by a medium priority task effectively "inverting" the relative priorities of the two tasks.

This violates the priority model that high priority tasks can only be prevented from running by higher priority tasks and briefly by low priority tasks which will quickly complete their use of a resource shared by the high and low priority tasks.

 

Example of a Priority Inversion

 

Consider there is a task L, with low priority. This task requires resource R. Consider that L is running and it acquires resource R. Now, there is another task H, with high priority. This task also requires resource R. Consider H starts after L has acquired resource R. Now H has to wait until L relinquishes resource R. Everything works as expected up to this point, but problems arise when a new task M starts with medium priority at this instance in time.

At this stage, H is blocked on R, M is free to run, L has acquired R. Since M is highest priority unblocked task currently, it will be scheduled first and it will eat up all the processing power until it finishes, not allowing any other task to run. This would block L from running. Since L cannot run, L cannot relinquish R. Since R is still in use (by L), H cannot run. So as you see above, M will run till it is finished, then L will run - at least up to a point where it can relinquish R - and then H will run. Thus, in above scenario, tasks with lower priority run before task with high priority, effectively giving us a priority inversion.

In some cases, priority inversion can occur without causing immediate harm—the delayed execution of the high priority task goes unnoticed, and eventually the low priority task releases the shared resource. However, there are also many situations in which priority inversion can cause serious problems. If the high priority task is left starved of the resources, it might lead to a system malfunction or the triggering of pre-defined corrective measures, such as a watch dog timer resetting the entire system. The trouble experienced by the Mars lander "Mars Pathfinder" is a classic example of problems caused by priority inversion in realtime systems.

Priority inversion can also reduce the perceived performance of the system. Low priority tasks usually have a low priority because it is not important for them to finish promptly (for example, they might be a batch job or another non-interactive activity). Similarly, a high priority task has a high priority because it is more likely to be subject to strict time constraints—it may be providing data to an interactive user, or acting subject to realtime response guarantees. Because priority inversion results in the execution of the low priority task blocking the high priority task, it can lead to reduced system responsiveness, or even the violation of response time guarantees.

A similar problem called deadline interchange can occur within Earliest Deadline First Scheduling (EDF).

[edit]Solutions

 

The existence of this problem has been known since the 1970s, but there is no fool-proof method to predict the situation. There are however many existing solutions, of which the most common ones are:

Disabling all interrupts to protect critical sections

When disabled interrupts are used to prevent priority inversion, there are only two priorities: preemptible, and interrupts disabled. With no third priority, inversion is impossible. Since there's only one piece of lock data (the interrupt-enable bit), misordering locking is impossible, and so deadlocks cannot occur. Since the critical regions always run to completion, hangs do not occur. Note that this only works if all interrupts are disabled. If only a particular hardware device's interrupt is disabled, priority inversion is reintroduced by the hardware's prioritization of interrupts. A simple variation, "single shared-flag locking" is used on some systems with multiple CPUs. This scheme provides a single flag in shared memory that is used by all CPUs to lock all inter-processor critical sections with a busy-wait. Interprocessor communications are expensive and slow on most multiple CPU systems. Therefore, most such systems are designed to minimize shared resources. As a result, this scheme actually works well on many practical systems. These methods are widely used in simple embedded systems, where they are prized for their reliability, simplicity and low resource use. These schemes also require clever programming to keep the critical sections very brief, under 100 microseconds in practical systems. Many software engineers consider them impractical in general-purpose computers.

Arguably, these methods are similar to priority ceilings.

A priority ceiling

With priority ceilings, the shared mutex process (that runs the operating system code) has a characteristic (high) priority of its own, which is assigned to the task locking the mutex. This works well, provided the other high priority task(s) that tries to access the mutex does not have a priority higher than the ceiling priority.

Priority inheritance

Under the policy of priority inheritance, whenever a high priority task has to wait for some resource shared with an executing low priority task, the low priority task is temporarily assigned the priority of the highest waiting priority task for the duration of its own use of the shared resource, thus keeping medium priority tasks from pre-empting the (originally) low priority task, and thereby effectively the waiting high priority task as well. Once the resource is released, the low priority task continues at its original priority level.

 

大多数商用实时操作系统(RTOS)均采用基于优先级的抢先调度器,这些系统为每个任务分配唯一的优先等级。调度器可以保证在所有等待运行的任务中,真正运行的总是具有最高优先级的任务。为了满足上述目标,调度器需要在执行中抢先优先级较低的任务。
由于多个任务共享资源,调度器控制范围以外的事件可以在必要的情况下阻止具有最高优先级的准备就绪任务运行。如果出现这种情形,将有可能使任务错过临界期限(critical deadline),从而导致系统崩溃。优先级倒置就是当具有最高优先级的准备就绪任务在应该运行却无法执行时所采用的一项应急措施。

优先级倒置研究获得了两种解决方案。第一种方案称为优先级继承(priority inheritance),该技术强令低优先级的任务继承与之共享资源并被挂起的任意高优先级任务的优先等级。一旦高优先级任务开始挂起,即可实施优先级继承,直到资源释放。这需要得到操作系统的大力支持。


第二种解决方案称为优先级顶置(priority ceiling),该方案为每种资源都分配优先级;调度器将该优先级传送至任何存取资源的任务,而分配给资源的优先级则为最高优先级用户的优先级。一旦任务完成对该资源的操作,其优先级恢复正常。 
优先级顶置的一大特色就在于任务可以共享资源,并且只需简单地改变资源的优先级,因此就不再需要信号量。