简介
信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。
队列和信号量的区别
- 队列可以容纳多个数据,信号量仅存放数值,无法存放其他数据;
- 创建队列有两部分内存,队列结构体+队列项存储空间,创建信号量只需分配信号量结构体;
- 写入队列,当队列满时可阻塞,释放信号量不可阻塞,计数值++,当计数值为最大值时返回失败;
- 读取队列,当队列为空时,可阻塞,获取信号量,计数值–,当没有资源是可阻塞;
二值信号量
二值信号量的本质是一个队列长度为1的队列,该队列就只有空和满两种情况,这就是二值。
二值信号量通常用于互斥访问或任务同步,与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题,所以二值信号量更适合用于同步。
相关API
//创建信号量 返回值NULL 创建失败,其他值创建成功返回二值信号量的句柄
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, ( pxStaticSemaphore ), queueQUEUE_TYPE_BINARY_SEMAPHORE )
//释放信号量 返回值 pdPASS释放成功,其他值释放失败
#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )
//获取二值信号量 xSemaphore 获取的信号量句柄 xBlockTime 阻塞时间
//返回值 pdTRUE获取信号量成功,pdFALSE 超时,获取信号量失败
#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), NULL, ( pxHigherPriorityTaskWoken ) )
代码示例
#include "queue.h"
#inculde "sempht.h"
SemaphoreHandle_t binarySem_handle = NULL;
void init(){
binarySem_handle = xSemaphoreCreateBinary();
}
void task1(){
while(1){
xSemaphoreGive(binarySem_handle);//释放信号量
}
}
void task2(){
while(1){
xSemaphoreTake(binarySem_handle,portMAX_DELAY); //获取信号量
}
}
计数型信号量
计数型信号量相当于队列长度大于1的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的。
适用场合
- 事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量,其他任务会获取计数型信号量,这种场合一般在创建时初始计数值设置为0。
- 资源管理:信号量表示有效的资源数目,任务必须先获取信号量,才能获取资源控制权,当计数值减为零时表示没有资源,当任务使用完资源后,必须释放信号量,信号量创建时计数值应等于最大资源数目。
相关API
//uxMaxCount 计数值的最大值限定 uxInitialCount 计数值的初始值
//返回NULL创建失败,其他值创建成功返回信号量句柄
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
#define xSemaphoreCreateCountingStatic( uxMaxCount, uxInitialCount, pxSemaphoreBuffer ) xQueueCreateCountingSemaphoreStatic( ( uxMaxCount ), ( uxInitialCount ), ( pxSemaphoreBuffer ) )
//获取信号量的计数值
//返回值 当前信号量的计数值大小
#define uxSemaphoreGetCount( xSemaphore ) uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )
代码示例
#include "queue.h"
#inculde "sempht.h"
SemaphoreHandle_t countSem_handle = NULL;
void init(){
countSem_handle = xSemaphoreCreateCounting(5,5);
}
void task1(){
while(1){
xSemaphoreGive(binarySem_handle);//释放信号量
}
}
void task2(){
while(1){
xSemaphoreTake(binarySem_handle,portMAX_DELAY); //获取信号量
}
}
优先级翻转
高优先级的任务反而慢执行,低优先级的任务反而优先执行。
优先级翻转在抢占式内核中时非常常见的,但是在实时操作系统中是不允许优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。
在使用二值信号量的时候,经常会遇到优先级翻转的问题。
互斥信号量
互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最合适,互斥信号量适合用于那些需要互斥访问的应用中。
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时,如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞,不过这个高优先级的任务会将低优先级的任务的优先级提升到与自己相同的优先级。
优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响;
互斥信号量不能用于中断服务函数中,原因如下
- 互斥信号量有任务优先级继承的机制,但是中断不是任务,没有任务优先级,所以互斥信号量只能用于任务中,不能用于中断服务函数。
- 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
相关API
//返回值NULL 创建失败 其他值 创建成功返回句柄
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#define xSemaphoreCreateMutexStatic( pxMutexBuffer ) xQueueCreateMutexStatic( queueQUEUE_TYPE_MUTEX, ( pxMutexBuffer ) )
代码示例
#include "queue.h"
#inculde "sempht.h"
SemaphoreHandle_t muxSem_handle = NULL;
void init(){
muxSem_handle = xSemaphoreCreateMutex();
}
void task1(){
BaseType_t xReturn = pdPASS;
while(1){
xReturn = xSemaphoreTake(muxSem_handle ,portMAX_DELAY); //获取信号量
xReturn = xSemaphoreGive(muxSem_handle );//释放信号量
}
}
void task2(){
BaseType_t xReturn = pdPASS;
while(1){
xReturn = xSemaphoreTake(muxSem_handle ,portMAX_DELAY); //获取信号量
xReturn = xSemaphoreGive(muxSem_handle );//释放信号量
}
}