目录
信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。其中,“同步”指的
是任务间的同步,即信号量可以使得一个任务等待另一个任务完成某件事情后,才继续执行;
而“有序访问”指的是对被多任务或中断访问的共享资源(如全局变量)的管理,当一个任务
在访问(读取或写入)一个共享资源时,信号量可以防止其他任务或中断在这期间访问(读取
或写入)这个共享资源。
需要包含的头文件为"semphr.h "
二值信号量
信号量是基于队列实现的,二值信号量也不例外,二值信号量实际上就是一个队列长度为 1 的队列,在这种情况下,队列就只有空和满两种情况,这不就是二值情况吗。
事实上RTOS正是这样做的:
在创建二值信号量的函数xSemaphoreCreateBinaryStatic( )里跳转进入可以看到它是一个宏定义,会在预处理阶段直接替换为xQueueGenericCreateStatic( )静态创建了一个大小为1的队列
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
#define xSemaphoreCreateBinaryStatic( pxStaticSemaphore ) xQueueGenericCreateStatic( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, ( pxStaticSemaphore ), queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif /* configSUPPORT_STATIC_ALLOCATION */
二值信号量相关API函数
函数
|
描述
|
xSemaphoreCreateBinary()
|
使用动态方式创建
二值信号量
|
xSemaphoreCreateBinaryStatic()
|
使用静态方式创建二值信号量
|
xSemaphoreTake()
|
获取信号量
|
xSemaphoreTakeFromISR().
|
在中断中获取信号量
|
xSemaphoreGive()
|
释放信号量
|
xSemaphoreGiveFromISR()
|
在中断中释放信号量
|
vSemaphoreDelete()
|
删除信号量
|
一般使用方法为
1.创建二值信号量
QueueHandle_t xSemaphore;//声明二值信号量
xSemaphore = xSemaphoreCreateBinary();//创建二值信号量
2.在一个任务中置为二值释放信号
xSemaphoreGive(xSemaphore);//释放信号
3.在另一个任务中获取信号
xSemaphoreTake(xSemaphore, portMAX_DELAY);//获得信号量
注意在等待信号量的任务中应该为
while(1){
if(xSemaphoreTake(xSemaphore, portMAX_DELAY);//阻塞等待
{
... /*具体操作*/
}
}
从而达到任务阻塞等待信号的作用
计数信号量
计数型信号量与二值信号量类似,二值信号量相当于队列长度为 1 的队列,因此二值信号
量只能容纳一个资源,这也是为什么命名为二值信号量,而计数型信号量相当于队列长度大于
0 的队列,因此计数型信号量能够容纳多个资源,这是在计数型信号量被创建的时候确定的。
计数型信号量通常用于一下两种场合:
1. 事件计数
每次事件发生后,在事件处理函数中释放计数型信号量(计数型信号量的
资源数加 1),其他等待事件发生的任务获取计数型信号量(计数型信号量的资源数减 1),这么
一来等待事件发生的任务就可以在成功获取到计数型信号量之后执行相应的操作。在这种场合
下,计数型信号量的资源数一般在创建时设置为 0。
2. 资源管理
计数型信号量的资源数代表着共享资源的可用数量,例如前面举例中停车
场中的空车位。一个任务想要访问共享资源,就必须先获取这个共享资源的计数型信号量,之
后在成功获取了计数型信号量之后,才可以对这个共享资源进行访问操作,当然,在使用完共
享资源后也要释放这个共享资源的计数型信号量。在这种场合下,计数型信号量的资源数一般
在创建时设置为受其管理的共享资源的最大可用数量。
计数型信号量相关 API 函数
函数
|
描述
|
xSemaphoreCreateCounting()
|
使用动态方式创建计数型信号量
|
xSemaphoreCreateCountingStatic()
|
使用静态方式创建计数型信号量
|
xSemaphoreTake()
|
获取信号量
|
xSemaphoreTakeFromISR()
|
在中断中获取信号量
|
xSemaphoreGive()
|
释放信号量
|
xSemaphoreGiveFromISR()
|
在中断中释放信号量
|
vSemaphoreDelete()
|
删除信号量
|
使用方法
1.创建计数信号量
QueueHandle_t CountSemaphore;/声明计数信号量
CountSemaphore=xSemaphoreCreateCounting( (UBaseType_t)255, (UBaseType_t)0);
/*计数信号量最大255,初始为0*/
2.释放计数信号量
/* 释放计数型信号量 */
xSemaphoreGive(CountSemaphore);
3.获得信号量的当前值
uint32_t semaphore_val = uxSemaphoreGetCount(CountSemaphore);//获得信号量值
printf("当前计数信号量%d\r\n",semaphore_val);
4.释放信号量
xSemaphoreTake(CountSemaphore,(TickType_t )portMAX_DELAY);//阻塞最大时间portMAX_DELAY
互斥信号量
互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中(任务与任务或
中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中。
在互斥访问中互斥信号量相当于一把钥匙,当任务想要访问共享资源的时候就必须先获得这把
钥匙,当访问完共享资源以后就必须归还这把钥匙,这样其他的任务就可以拿着这把钥匙去访
问资源。
首先要介绍以下实时性问题里的”优先级翻转“
优先级翻转的示例中,,任务 H 为最高优先级的任务,因此任务 H 执行的操作需要有较高的实时性,但是由于优先级翻转的问题,导致了任务 H 需要等到任务 L 释放信号量才能够运行,并且,任务 L 还会被其他介于任务 H 与任务 L 任务优先级之间的任务 M 抢占,因此任务 H 还需等待任务 M 运行完毕,这显然不符合任务 H 需要的高实时性要求。
然而互斥信号量的特性中优先级继承,优先级继承尽可能的减少了高优先级任务处于阻塞态
的时间,并且将“优先级翻转”的影响降到最低。
优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的
影响。实时应用应该在设计之初就要避免优先级翻转的发生。互斥信号量不能用于中断服务函
数中,原因如下:
(1) 互斥信号量有任务优先级继承的机制,但是中断不是任务,没有任务优先级,所以互斥
信号量只能用与任务中,不能用于中断服务函数。
(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
互斥信号量常用API函数
函数
|
描述
|
xSemaphoreCreateMutex()
|
使用动态方式创建互斥信号量
|
xSemaphoreCreateMutexStatic()
|
使用静态方式创建互斥信号量
|
xSemaphoreTake()
|
获取信号量
|
xSemaphoreGive() |
释放信号量
|
vSemaphoreDelete()
|
删除信号量
|
从上面中可以看出,互斥信号量除了创建函数之外,其余的获取、释放等信号量操作函数,
都与二值信号量相同。
使用方法
同二值信号量