多任务间同步的方式包括关中断,调度器上锁, 互斥量(mutex),信号量,消息队列几种, 其中互斥量和信号量使用使用方式相近,连概念和实现都有些近似, 所以没有扎实操作系统背景知识的同学常常分不清出他们的区别,容易混淆, 下面简单分析以下.
1.互斥量
互相斥量是管理临界资源的一种有效手段, 因为互斥量是独占的, 所以在一个时刻只允许一个线程占有互斥量,利用这个性质来实现共享资源的互斥量保护,任何时刻只允许一个线程获得互斥量对象,未能够获得互斥量对象的线程被挂起在该互斥量的等待线程队列上,这一点和1资源信号量是相同的, 但互斥量有所有者的概念,高优先级的任务可以在获取互斥量时通过对比所有者的优先级是否高于自己来决定是否提升所有者的优先级。 所以互斥量可以有效对付优先级反转的问题。
2.信号量
信号量是用来解决线程同步和互斥的通用工具, 和互斥量类似, 信号量也可以用作自于资源互斥访问, 但信号量没有所有者的概念,在应用上比互斥量更广泛,信号量比较简单, 不能解决优先级反转问题,但信号量是一种轻量级的对象,比互斥量小巧,灵活,因此在很多对互斥要求不严格的的系统中,经常使用信号量来管理互斥资源。如果定义的信号量表示一种资源,则它是用来同步的,如果信号量定义成一把锁,则它是用来保护的。
小结:
关于所有者的概念,最开始我也不是很清楚它的内涵,最近看了一下FreeRTOS 信号量头文件的一些注释,有些感悟和收获,注释原文在 freertoscode/source/include/semphr.h中。
* This type of semaphore can be used for pure synchronisation between tasks or
* between an interrupt and a task. The semaphore need not be given back once
* obtained, so one task/interrupt can continuously 'give' the semaphore while
* another continuously 'takes' the semaphore. For this reason this type of
* semaphore does not use a priority inheritance mechanism. For an alternative
* that does use priority inheritance see xSemaphoreCreateMutex().
*
大概意思是说, sem和mutex的使用方式不同, 对于同一个上下文内来说,mutex的获取和释放必须成对调用, 获取次数和释放次数必须一样,强调所有权的概念。
sem则没有这些限制,可以一个上下文执行释放,另一个上下文执行获取, 强调的是资源数的概念,至于资源是谁发出的,没有限制,同等对待,获取了不去释放,也无关紧要,反正不获取也能发出,资源可以无中生有.
总结成一句话就是:sem可以“无中生有",但是mutex不可以,创建时候多少个,就固定为多少个。
补充说明:
很多时候,其实信号量和互斥量是可以互换使用的,证据就是,在Linux早期,就经历过sem到mutex切换的过程,证据就是下面这段文字:
互斥锁最早是在2.6.16内核中由Red Hat Enterprise Linux的资深内核专家Ingo Molnar设计和实现的,信号量的count成员可以初始化为1,并且down和up也可以实现互斥锁的功能,那为什么要单独实现互斥锁呢?Ingo Molnar认为,在设计之初,信号量在Linux内核中的实现没有任何问题,但是互斥锁相对于信号量要简单轻便,在锁争用激烈的情况下,互斥锁比信号量执行速度更快,可扩展性更好,另外,mutex数据结构的定义比信号量要小,这些都是互斥锁设计之初Ingo Molnar提到的优点,互斥锁上的一些优化方案,已经移植到了读写信号量中。