Mutex与Semaphore 第三部分:互斥的问题

原文:
https://blog.feabhas.com/2009/10/mutex-vs-semaphores-%E2%80%93-part-3-final-part-mutual-exclusion-problems/

As hopefully you can see from the previous posting, the mutex is a significantly safer mechanism to use for implementing mutual exclusion around shared resources. Nevertheless, there are still a couple of problems that use of the mutex (in preference to the semaphore) will not solve. These are:

Circular deadlock
Non-cooperation

正如你在之前的文章中看到的那样,对于共享的公共资源,互斥锁时做互斥访问更安全的一个工具。然而,依然有2个问题没有解决:
* 循环死锁
* 不合作

Circular Deadlock

Circular deadlock, often referred to as the “deadly embrace” problem is a condition where two or more tasks develop a circular dependency of mutual exclusion. Simply put, one task is blocked waiting on a mutex owned by another task. That other task is also block waiting on a mutex held by the first task.

So how can this happen? Take as an example a small control system. The system is made up of three tasks, a low priority Control task, a medium priority System Identification (SI) task and a high priority Alarm task. There is an analogue input shared by the Control and the SI tasks, which is protected by a mutex. There is also an analogue output protected by a different mutex.
这里写图片描述

循环死锁

循环死锁,常被称为“死亡拥抱”。两个或多个任务对互斥资源有循环依赖。简而言之,一个任务被阻塞,依赖于其他任务去释放。而又有其他任务阻塞在第一个任务持有的锁上。
这是怎么发生的呢?用一个小型控制系统举例,系统包含3个任务,第一个是低优先级的控制任务,第二个是中优先级的认证任务和高优先级的报警任务。有一个模拟的输入,由控制任务和认证任务共享,用互斥锁1来保护。同时也有一个模拟的输出由他们共享,用另一个互斥锁2来保护。

The Control task waits for mutexes ADC and DAC:

mutex_lock (ADC);
mutex_lock (DAC);
/* critical section */
mutex_unlock (ADC);
mutex_unlock (DAC);
The SI Task waits for mutexes DAC and ADC:

mutex_lock (DAC);
mutex_lock (ADC);
/* critical section */
mutex_unlock (DAC);
mutex_unlock (ADC);

Unfortunately, under certain timing conditions, this can lead to deadlock. In this example the Control task has locked the ADC, but before locking the DAC has been pre-empted by the higher priory SI task. The SI task then locks the DAC and tries to lock the ADC. The SI task is now blocked as the ADC is already owned by the Control task. The Control task now runs and tries to lock the DAC. It is blocked as the DAC is held by the SI task. Neither task can continue until the mutex is unlocked and neither mutex can be unlocked until either task runs – classic deadlock.

控制任务等待ADC和DAC的互斥锁:

mutex_lock (ADC);
mutex_lock (DAC);
/* critical section */
mutex_unlock (ADC);
mutex_unlock (DAC);

认证任务也等待DAC和ADC的互斥锁:

mutex_lock (DAC); 
mutex_lock (ADC); 
/* critical section */ 
mutex_unlock (DAC); 
mutex_unlock (ADC);

不幸的是,在特定的时间条件下,这可能导致死锁。在这个例子中,控制任务已经持有ADC,但在持有DAC之前被更高优先级的认证任务抢占,认证任务于是持有了DAC,并试图获取ADC。认证任务被阻塞,因为ADC已经被控制任务持有。控制任务这时获得了CPU开始运行,并试图获取DAC,它将被阻塞,因为DAC被认证任务持有。每个任务都不能继续运行,直到所需的锁被释放,而每个锁都没法释放,直到任务运行——这就是典型的死锁。

这里写图片描述

For circular deadlock to occur the following conditions must all be true:

A thread has exclusive use of resources (Mutual exclusion)
A thread can hold on to a resource(s) whilst waiting for another resource (Hold and wait)
A circular dependency of thread and resources is set up (Circular waiting)
A thread never releases a resource until it is completely finished with it (No resource preemption)
These conditions can be addressed in a number of ways. For example, a design policy may stipulate that if a task needs to lock more than one mutex it must either lock all or none.

循环死锁要发生,必须满足以下4个条件:
* 线程独占资源(互斥条件)
* 线程会保持目前的资源,并请求其他需要的资源(请求与保持条件)
* 线程之间存在循环等待(环路等待条件)
* 线程在完成工作之前不会释放已有的资源(资源不可抢占条件)
这些条件可以用很多不同的方式破解。例如,某个设计策略可能会规定,任务如果需要多个资源,那么它要不得到全部,要不一个都不得到。

Priority Ceiling Protocol

With the Priority Ceiling Protocol (PCP) method each mutex has a defined priority ceiling, set to that of the highest priority task which uses the mutex. Any task using a mutex executes at its own priority – until a second task attempts to acquire the mutex. At this point it has its priority raised to the ceiling value, preventing suspension and thus eliminating the “hold and wait” condition.

In the deadlock example shown before, the significant point is when the SI task tries to lock the DAC. Before that succeeded and lead to cyclic deadlock. However with a PCP mutex, both the ADC and DAC mutex will have a ceiling priority equal to the SI’s task priority. When the SI task tries to lock the DAC, then the run-time system will detect that the SI’s task priority is not higher than the priority of the locked mutex ADC. The run-time system suspends the SI task without locking the DAC mutex. The control task now inherits the priority of the SI task and resumes execution.

优先级天花板协议

使用优先级天花板协议(PCP),每个互斥锁都有最大优先级,设为需要这个锁的任务的优先级的最大值。每个使用该锁的任务都已其自己的优先级运行,直到有其他任务请求这个锁。这个时候,持有锁的任务的优先级会提高到锁的天花板值,用以防止该任务被调度出去,从而消除“请求与保持”条件。
在刚才展示的死锁的例子中,认证任务试图获取DAC是很重要的时间点,获取完以后,系统就走向了死锁。然而,如果使用的是PCP互斥锁,ADC和DAC都有一个天花板值,都等于认证任务的优先级。当认证任务试图获取DAC时,运行时系统就会检测到,该任务的优先级并不大于它已获取的ADC的优先级,于是系统将认证任务调度出去,并没有给他锁。控制任务于是继承了认证任务的优先级,并继续运行。
这里写图片描述

Non-cooperation

The last, but most important aspect of mutual exclusion covered in these ramblings relies on one founding principle: we have to rely on all tasks to access critical regions using mutual exclusion primitives. Unfortunately this is dependent on the design of the software and cannot be detected by the run-time system. This final problem was addressed by Tony Hoare, called the Monitor.

不合作

互斥锁的最后一个,不过也是最重要的特点在于:我们必须依赖于,所有的任务在访问临界区时都会使用互斥原语。不幸的是,这一点依赖于软件的设计,不能够在运行时被检测出来。这个问题最终是被Tony Hoare解决的,称为监视器。

The Monitor

The monitor is a mechanism not typically supplied by the RTOS, but something the programmer tends to build (a notable exception is Ada95’s protected object mechanism). A monitor simply encapsulates the shared resource and the locking mechanism into a single construct (e.g. a C++ Object that encapsulates the mutex mechanism). Access to the shared resource, then, is through a controlled interface which cannot be bypassed (i.e. the application never explicitly calls the mutex, but calls upon access functions).

监视器

监视器是一种机制,一般不是RTOS提供的,而是程序员自己构建的(Ada95的对象保护机制是个例外)。监视器通常只是简单地将共享资源和保护机制封装在一起(比如将互斥器封装在C++的对象里)。对于共享资源的访问,是通过不能被绕过的受控接口进行的。(也就是说,应用层并不显式调用互斥器,而是通过函数去间接调用)

Finishing Off…

This goal of these initial postings is to demonstrate that common terms used in the real-time programming community are open to ambiguity and interpretation. Hopefully you should now be clear about the core differences between the Binary Semaphore, General (counting) Semaphore and the Mutex.

The underlying difference between the Semaphores and the Mutex is the Principle of Ownership. Given the principle of ownership a particular implementation of a mutex may support Recursion, Priority inheritance and Death Detection.

结束

这一系列的帖子是为了阐述,在实时系统编程社区中使用的一些术语对歧义和解释是开放的。希望看到这里,你应该对二进制信号量,计数信号量和互斥锁的区别有了清晰的认识。
信号量和互斥锁的底层区别在于有没有持有者。有了持有者,互斥锁的实现就可以支持递归,优先级继承,以及死亡检测。

ENDNOTE

An aspect of the mutex I haven’t covered here is that many operating systems support the concept of a condition variable. A condition variable allows a task to wait on a synchronization primitive within a critical region. The whole aspect Synchronization Patterns (e.g. semaphore as a signal) within the context of RTOSs will be the subject of my next posting.

后记

互斥锁有个我没讲到的方面,很多操作系统支持条件变量的概念。一个条件变量允许任务等待一个同步原语,以进入临界区。实时系统中的同步模式(如用作信号的信号量)的完整论述会在我的下一篇文章中讲到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值