Mutex与Semaphore 第二部分 互斥锁

原文链接:
https://blog.feabhas.com/2009/09/mutex-vs-semaphores-%E2%80%93-part-2-the-mutex/

In Part 1 of this series we looked at the history of the binary and counting semaphore, and then went on to discuss some of the associated problem areas. In this posting I aim to show how a different RTOS construct, the mutex, may overcome some, if not all, of these weaknesses.

To address the problems associated with semaphore, a new concept was developed during the late 1980’s. I have struggled to find it’s first clear definition, but the major use of the term mutex (another neologism based around MUTual EXclusion) appears to have been driven through the development of the common programming specification for UNIX based systems. In 1990 this was formalised by the IEEE as standard IEEE Std 1003.1 commonly known as POSIX.

在第一部分中,我们回顾了二值信号量与计数信号量的历史,并且讨论了信号量的几个问题。在本文中,我将致力于阐述RTOS中的另一个结构——互斥锁,是如何解决信号量的大部分问题的——如果没解决全部问题的话。

为了解决信号量的问题,1980年代出现了一个新概念。我尽力去找互斥锁(MUTEX,用MUTual和EXclusion组合出的新词)这个词的第一次完整定义,却发现它是在开发类UNIX系统的编程规范时就出现了。1990年,被IEEE纳入IEEE Std 1003.1标准,这就是POSIX。

The mutex is similar to the principles of the binary semaphore with one significant difference: the principle of ownership. Ownership is the simple concept that when a task locks (acquires) a mutex only it can unlock (release) it. If a task tries to unlock a mutex it hasn’t locked (thus doesn’t own) then an error condition is encountered and, most importantly, the mutex is not unlocked. If the mutual exclusion object doesn’t have ownership then, irrelevant of what it is called, it is not a mutex.

互斥锁与二值信号量有些类似,除了一个关键点以外:持有者。持有者这个概念很简单,当一个任务持有互斥锁时,只有这个任务才能解开这个互斥锁。当一个任务试图解锁一个它不持有的互斥锁时,会引发一个错误,而且,最重要的是,这个互斥锁不会被解锁。如果一个互斥锁对象不包含它的持有者,那么无论它叫什么学名,都不是一个真正的互斥锁。

The concept of ownership enables mutex implementations to address the problems discussed in part 1:

Accidental release
Recursive deadlock
Task-Death deadlock
Priority inversion
Semaphore as a signal

持有者的概念使得互斥锁可以解决第一篇文章中提到的问题:
* 意外释放
* 递归引起的死锁
* 任务退出引起的死锁
* 优先级反转
* 信号量被用作信号

Accidental Release

As already stated, ownership stops accidental release of a mutex as a check is made on the release and an error is raised if current task is not owner.

意外释放

如上文所述,持有者的概念解决了意外释放问题。释放时,系统会检查当前任务是否是互斥锁的持有者,如果不是就报错。

Recursive Deadlock

Due to ownership, a mutex can support relocking of the same mutex by the owning task as long as it is released the same number of times.

递归引起的死锁

由于有了持有者,任务可以多次获取同一个互斥锁,只要它释放同样多的次数即可。

Priority Inversion

With ownership this problem can be addressed using one of the following priority inheritance protocols:

[Basic] Priority Inheritance Protocol
Priority Ceiling Protocol
The Basic Priority Inheritance Protocol enables a low-priority task to inherit a higher-priorities task’s priority if this higher-priority task becomes blocked waiting on a mutex currently owned by the low-priority task. The low priority task can now run and unlock the mutex – at this point it is returned back to its original priority.

The details of the Priority Inheritance Protocol and Priority Ceiling Protocol (a slight variant) will be covered in part 3 of this series.

优先级反转

有了持有者,可以用以下优先级继承协议来处理优先级反转:
* 基本优先级继承协议
* 优先级天花板协议
在基本优先级继承协议中,如果高优先级任务阻塞在一个互斥锁中,而锁被一个低优先级任务持有,那么这个低优先级任务就会继承高优先级任务的优先级。于是低优先级任务就会被调度运行,然后释放锁,这时其优先级就恢复到原来的优先级了。
这两个协议的详细信息会在这个系列的第三篇文章中给出。

Death Detection

If a task terminates for any reason, the RTOS can detect if that task current owns a mutex and signal waiting tasks of this condition. In terms of what happens to the waiting tasks, there are various models, but two doiminate:

All tasks readied with error condition;
Only one task readied; this task is responsible for ensuring integrity of critical region.
When all tasks are readied, these tasks must then assume critical region is in an undefined state. In this model no task currently has ownership of the mutex. The mutex is in an undefined state (and cannot be locked) and must be reinitialized.

When only one task is readied, ownership of the mutex is passed from the terminated task to the readied task. This task is now responsible for ensuring integrity of critical region, and can unlock the mutex as normal.

死亡检测

如果任务因故终止,RTOS会检测该任务是否持有互斥锁,并向等待这个锁的任务发出信号。关于等待的任务会发生什么,有多种模型,不过以以下2种为主:
* 所有任务被唤醒
* 只唤醒一个任务
如果所有任务都被唤醒,这些任务必须假定临界区处于未定义状态。在这个模型中,所有被唤醒的任务都不持有互斥锁。互斥锁也处于未定义状态,不能被获取,必须重新初始化。
如果只唤醒一个任务,那么互斥锁的持有者会从终止的任务转移到这个任务上。该任务必须保证临界区的完整性,(执行完临界区后)可以照常释放锁。

Mutual Exclusion / Synchronisation

Due to ownership a mutex cannot be used for synchronization due to lock/unlock pairing. This makes the code cleaner by not confusing the issues of mutual exclusion with synchronization.

互斥/同步

由于持有者的概念,互斥锁不能用于同步,因为加锁/解锁必须成对出现。这使代码非常清晰,我们不再将同步和互斥搞混了。

Caveat

A specific Operating Systems mutex implementation may or may not support the following:

  • Recursion
  • Priority Inheritance
  • Death Detection

警告

操作系统的互斥锁实现可以选择支持或不支持以下特性:
* 递归
* 优先级继承
* 死亡检测

Review of some APIs

It should be noted that many Real-Time Operating Systems (or more correctly Real-Time Kernels) do not support the concept of the mutex, only supporting the Counting Semaphore (e.g. MicroC/OS-II). [ CORRECTION: The later versions of uC/OS-II do support the mutex, only the original version did not].

In this section we shall briefly examine three different implementations. I have chosen these as they represent the broad spectum of APIs offered (Footnote 1):

  • VxWorks Version 5.4
  • POSIX Threads (pThreads) – IEEE Std 1003.1, 2004 Edition
  • Microsoft Windows Win32 – Not .NET

VxWorks from Wind River Systems is among the leading commercial Real-Time Operating System used in embedded systems today. POSIX Threads is a widely supported standard, but has become more widely used due to the growth of the use of Embedded Linux. Finally Microsoft Window’s common programming API, Win32 is examined. Windows CE, targeted at embedded development, supports this API.

However, before addressing the APIs in detail we need to introduce the concept of a Release Order Policy. In Dijkstra’s original work the concept of task priorities were not part of the problem domain. Therefore it was assumed that if more than one task was waiting on a held semaphore, when released the next task to acquire the semaphore would be chosen on a First-Come-First-Server (First-In-First-Out; FIFO) policy. However once tasks have priorities, the policy may be:

  • FIFO – waiting tasks ordered by arrival time
  • Priority – waiting tasks ordered by priority
  • Undefined – implementation doesn’t specify

回顾部分API

注意,很多实时系统并不支持互斥锁的概念,只支持计数信号量。(如MicroC/OS-II)[更正:uC/OS-II的最新版已经支持了,只有最初的版本不支持].
这一部分我们将简要看一下3个互斥锁的不同实现,之所以选择这3个是因为他们覆盖了现有API的大部分特点。

  • VxWOrks5.4
  • POSIX线程。IEEE STd 1003.1, 2004版
  • 微软Win32 -不是.NET
    Wind River Systems的VxWorks是当今嵌入式系统使用的商业实时系统市场的领导者。POSIX线程是广泛支持的标准,由于嵌入式Linux的增长,它的使用也更多了。最后是Win32,因为嵌入式开发使用的WinCE支持这套API。

然而,在详细介绍API之前,我们需要引入释放顺序策略(Release Order Policy)的概念。Dijkstra最初提出的任务优先级与这个不相关。所以,这里先假定,如果有多个任务在等待一个被持有的互斥锁,那么当锁释放时,这些任务的唤醒策略是先进先出(FIFO)。然而,一旦任务有了优先级,策略就可能是:
* 先进先出 - 等待的任务按到达的时间排序
* 优先级 -等待的任务按优先级排序
* 未定义 - 实现中尚未指定

VxWorks v5.4

VxWorks supports the Binary Semaphore, the Counting Semaphore and the Mutex (called the Mutual-Exclusion Semaphore in VxWorks terminology). They all support a common API for acquiring (semTake) and releasing (semGive) the particular semaphore. For all semaphore types, waiting tasks can be queued by priority or FIFO and can have a timeout specified.

The Binary Semaphore has, as expected, no support for recursion or inheritance and the taker and giver do not have to be same task. Some additional points of interest are that there is no effect of releasing the semaphore again; It can be used as a signal (thus can be created empty); and supports the idea of a broadcast release (wake up all waiting tasks rather than just the first). The Counting Semaphore, as expected, is the same as the Binary Semaphore with ability to define an initial count.

The Mutual-Exclusion Semaphore is the VxWorks mutex. Only the owning task may successfully call semGive. The VxWorks mutex also has the ability to support both priority inheritance (basic priority inheritance protocol) and deletion safety.

VxWorks 5.4

VxWorks支持二值信号量,计数信号量以及互斥锁(被称为互斥信号量)。他们均支持获取(semTake)和释放(semGive)。对于所有类型的信号量,等待的任务可以按优先级也可以按先入先出的顺序排序,并可以指定超时。

二值信号量不支持递归和优先级继承,take和give不必须是同一个任务。添加了其他特性,如重复give不会引起错误和副作用。可以被用作信号(这样创建时初始化为空即可)。还可以广播释放,即释放所有等待的任务,而不是只释放一个。计数信号量与二值信号量类似,只是可以指定任意初始值。

互斥信号量就是VxWorks中的互斥锁。只有当前任务才能释放锁,同时还支持优先级继承(基本优先级继承协议)和死亡安全监测。

POSIX

POSIX is an acronym for Portable Operating System Interface (the X has no meaning). The current POSIX standard is formally defined by IEEE Std 1003.1, 2004 Edition. The mutex is part of the core POSIX Threads (pThreads) specification (historically referred to as IEEE Std 1003.1c-1995).
POSIX also supports both semaphores and priority-inheritance mutexes as part of what are called Feature Groups. Support for these Feature Groups is optional, but when an implementation claims that a feature is provided, all of its constituent parts must be provided
and must comply with this specification. There are two main Feature Groups of interest, the Realtime Group and Realtime Threads Groups.

The semaphore is not part of the core standard but is supported as part of the Realtime Feature Group. The Realtime Semaphore is an implementation of the Counting semaphore.

The default POSIX mutex is non-recursive , has no priority inheritance support or death detection.
However, the Pthreads standard allows for non-portable extensions (as long as they are tagged with “-np”). A high proportion of programmers using POSIX threads are programming for Linux. Linux supports four different mutex types through non-portable extensions:

Fast mutex – non-recursive and will deadlock [default]
Error checking mutex – non-recursive but will report error
Recursive mutex – as the name implies
Adaptive mutex – extra fast for mutli-processor systems
These are extreamly well covered by Chris Simmonds in his posting Mutex mutandis: understanding mutex types and attributes.

Finally the Realtime Threads Feature Group adds mutex support for both priority inheritance and priority ceiling protocols.

POSIX

POSIX是 Portable Operating System Interface 的缩写,X没什么含义。当前的POSIX标准是IEEE Std 1003.1, 2004。互斥锁时POSIX线程标准的一部分。
POSIX支持同时信号量和带优先级继承的互斥锁,这被称为特性组合。特性组合对实现来说是可选的,但若某个实现声称提供某个特性,则必须完全实现该特性并符合规范。目前有2个组:实时组和实时线程组。
信号量不是核心标准的一部分,但属于实时组的一部分,实时信号量是计数信号量的一种实现。
默认的POSIX互斥锁时不支持递归的,没有优先级继承也不支持死亡检测。然而,pthread标准允许一些“不可移植(non-portable)”的扩展,需要打上-np的选项。使用POSIX的程序员很大比例是Linux程序员。Linux通过不可移植扩展提供了4种互斥锁:
* 快速互斥锁 -不支持递归,一旦递归就死锁
* 错误检查互斥锁 -不支持递归,但会给出错误提示
* 递归互斥锁
* 适应性互斥锁 – 在多处理器系统中更快
这些信息在Chris Simmonds的文章 Mutex mutandis: understanding mutex types and attributes中有非常详细的解释。

最后,实时线程组为互斥锁支持了优先级继承协议和优先级天花板协议。

Win32 API

Microsoft Window’s common API is referred to as Win32. This API supports three different primitives:

Semaphore – The counting semaphore
Critical Section – Mutex between threads in the same process; Recursive, no timeout, queuing order undefined
Mutex – As per critical sections, but can be used by threads in different processes; Recursive, timeout, queuing order undefined
The XP/Win32 mutex API does not support priority inheritance in application code, however the WinCE/Win32 API does!

Win32 mutexes do have built-in death detection; if a thread terminates when holding a mutex, then that mutex is said to be abandoned. The mutex is released (with WAIT_ABANDONED error code) and a waiting thread will take ownership. Note that Critical sections do not have any form of death detection.

Critical Sections have no timeout ability, whereas mutexes do. However Critical Sections support a separate function call TryEnterCriticalSection. A major weakness of the Win32 API is that the queuing model is undefined (i.e. neither Priority nor FIFO). According to Microsoft this is done to improve performance.

So, what can we gather from this? First and foremost the term mutex is less well defined than the semaphore. Secondly,the actual implementations from RTOS to RTOS vary massively. I urge you to go back and look at your faviourite RTOS and work out what support, if any, you have for the mutex. I’d love to hear from people regarding mutual exclusion support (both semaphores and mutexes) for their RTOS of choice. If you’d like to contact me do so at nsc(at)acm.org.

Finally, Part 3 will look at a couple of problems the mutex doesn’t solve, and how these can be overcome. As part of that it will review the Basic Priority Inheritance Protcol and the Prority Ceiling Protocol.

At a later date I will also address the use of, and problems associted with, the semaphore being used for task synchronisation.

Win 32 API

Windows的API称为Win32,支持以下3种不同的原语:
* 信号量 - 计数信号量
* 临界区 –用于同一个进程的互斥锁,支持递归,不支持超时,等待的任务排序方式未定义
* 互斥锁 -类似临界区,但可用于不同进程中的线程,支持递归,超时,等待的任务排序方式未定义
XP/Win32的互斥锁不支持优先级继承,但WinCE/WIn32的API支持!
Win32的互斥锁有内建的死亡检测机制,如果线程在持有锁的时候挂了,那么该锁就会被抛弃,互斥锁会被释放(错误码为WAIT_ABANDONED),其中一个等待的任务会被唤醒。注意,“临界区”则没有死亡检测。
临界区没有超时机制,互斥锁有。但是,临界区支持一个额外的API,TryEnterCriticalSection。Win32 API的一个小缺点就是任务的排序方式是未定义的,既不是FIFO,也不是按优先级。根据MS的说法是为了提高性能。

那么我们能得到什么信息呢?首先,互斥锁这个名词的定义不像信号量那样统一。其次,不同的系统之间的实现是相差很多的。建议你回去用自己的RTOS检验一下到底支持哪些。我想听到人们对互斥支持(信号量,互斥锁)的RTOS的选择。可以通过nsc@acm.org联系我。
最后,系列文章的第三部分会关注互斥锁没有解决的2个问题,以及其解决方案。我们会在那里讲解基本优先级继承协议和优先级天花板协议。
晚些时候我会写信号量在同步过程中的用法和问题。

ENDNOTES

Please I do not want to get into the “that’s not a real-time OS” debate here – let’s save that for another day!
A number of people pointed out that Michael Barr (former editor of Embedded Systems Programming, now president of Netrino) has a good article about the differences between mutexes & semaphores at the following location: http://www.netrino.com/node/202. I urge you to read his posting as well.
Apologies about not having the atom feed sorted – this should all be working now

PS:
1.真心不想陷入“这是不是实时系统”的争论。有空再说这个事吧
2.很多人之初,Michael Barr(Embedded Systems Programming的前编辑,现在Netrino的总裁)有篇文章很好的区分了互斥锁和信号量: http://www.netrino.com/node/202,建议你们去看看。
3.抱歉提要没有排序

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值