信号量概念
是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问,常用于协助一组相互竞争的任务来访问临界资源。
信号量是用作计数的,标记,不需要关心队列中的消息。
临界资源
多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。
一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。
二值信号量运作机制
创建信号量时,系统会为创建的信号量对象分配内存,并把可用信号量初始化为用户自定义的个数, 二值信号量的最大可用信号量个数为 1
计数信号量运作机制
计数信号量可以用于资源管理,允许多个任务获取信号量访问共享资源,但会限制任务的最大数目。
访问的任务数达到可支持的最大数目时,会阻塞其他试图获取该信号量的任务,直到有任务释放了信号量。这就是计数型信号量的运作机制,虽然计数信号量允许多个任务访问同一个资源,但是也有限定,比如某个资源限定只能有 3 个任务访问,那么第 4 个任务访问的时候,会因为获取不到信号量而进入阻塞,等到有任务(比如任务 1)释放掉该资源的时候,第 4 个任务才能获取到信号量从而进行资源的访问,
信号量控制块
typedef struct QueueDefinition {
int8_t *pcHead;
int8_t *pcTail;
int8_t *pcWriteTo;
union {
int8_t *pcReadFrom;
UBaseType_t uxRecursiveCallCount;
} u;
List_t xTasksWaitingToSend;
List_t xTasksWaitingToReceive;
volatile UBaseType_t uxMessagesWaiting; //(1) 用来记录当前消息队列的消息个数;
UBaseType_t uxLength; (2)
UBaseType_t uxItemSize; (3)
volatile int8_t cRxLock;
volatile int8_t cTxLock;
#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
uint8_t ucStaticallyAllocated;
#endif
#if ( configUSE_QUEUE_SETS == 1 )
struct QueueDefinition *pxQueueSetContainer;
#endif
#if ( configUSE_TRACE_FACILITY == 1 )
UBaseType_t uxQueueNumber;
uint8_t ucQueueType;
#endif
} xQUEUE;
typedef xQUEUE Queue_t;
(2):如果控制块结构体是用于消息队列:uxLength 表示队列的长度, 也就是能存放多少消息;
如果控制块结构体被用于信号量的时候,uxLength 表示最大的信号量可用个数,会有以下两种情况:
- 如果信号量是二值信号量、互斥信号量,uxLength 最大为 1,因为信号量要么是有效的,要么是无效的。
- 如果是计数信号量,这个值表示最大的信号量个数,在创建计数信号量的时候将由用户指定这个值 uxMaxCount。
(3):如果控制块结构体是用于消息队列:uxItemSize 表示单个消息的大小;
如果控制块结构体被用于信号量的时候,则无需存储空间,为 0 即可。
常用信号量函数接口讲解
xSemaphoreCreateBinary()
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateBinary() \ 4 xQueueGenericCreate( \
( UBaseType_t ) 1, \ (1)
semSEMAPHORE_QUEUE_ITEM_LENGTH, \ (2) 7 queueQUEUE_TYPE_BINARY_SEMAPHORE ) (3)
#endif
QueueHandle_t xQueueGenericCreate(const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
const uint8_t ucQueueType )
xSemaphoreCreateCounting()
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) \
xQueueCreateCountingSemaphore((uxMaxCount),(uxInitialCount))
#endif
//下面是函数源码
#if( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
QueueHandle_t xQueueCreateCountingSemaphore(
const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount )
{
QueueHandle_t xHandle;
configASSERT( uxMaxCount != 0 );
configASSERT( uxInitialCount <= uxMaxCount );
xHandle = xQueueGenericCreate( uxMaxCount,
queueSEMAPHORE_QUEUE_ITEM_LENGTH,
queueQUEUE_TYPE_COUNTING_SEMAPHORE );
if ( xHandle != NULL )
{
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
traceCREATE_COUNTING_SEMAPHORE();
}
else
{
traceCREATE_COUNTING_SEMAPHORE_FAILED();
}
return xHandle;
}
#endif
/*-----------------------------------------------------------*/
实例:创建二值信号量
SemaphoreHandle_t xSemaphore = NULL;
2
3
4 void vATask( void * pvParameters )
5 {
6 /* 尝试创建一个信号量 */
7 xSemaphore = xSemaphoreCreateBinary();
8
9 if ( xSemaphore == NULL ) {
10 /* 内存不足,创建失败 */
11 } else {
12 /* 信号量现在可以使用,句柄存在变量 xSemaphore 中
13 这个时候还不能调用函数 xSemaphoreTake()来获取信号量
14 因为使用 xSemaphoreCreateBinary()函数创建的信号量是空的
15 在第一次获取之前必须先调用函数 xSemaphoreGive()先提交*/
16 } 17}
实例:创建计数信号量
1 void vATask( void * pvParameters )
2 {
3 SemaphoreHandle_t xSemaphore;
4 /* 创建一个计数信号量, 用于事件计数 */
5 xSemaphore = xSemaphoreCreateCounting( 5, 5 );
6
7 if ( xSemaphore != NULL ) {
8 /* 计数信号量创建成功 */
9 }
vSemaphoreDelete()
1 #define vSemaphoreDelete( xSemaphore ) \
2 vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
xSemaphoreGive()
#define xSemaphoreGive( xSemaphore ) \
2 xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), \
3 NULL, \
4 semGIVE_BLOCK_TIME, \
5 queueSEND_TO_BACK )
xSemaphoreGiveFromISR()(中断)
xSemaphoreTake()
实际上是一次消息出队操作,阻塞时间由用户指定xBlockTime,
当有任务试图获取信号量的时候,当且仅当信号量有效的时候,任务才能读获取到信号量。
如果信号量无效,在用户指定的阻塞超时时间中,该任务将保持阻塞状态以等待信号量有效。