信号量用于任务同步和控制共享资源的访问。信号量按照用途分为:计数信号量、二值信号量、互斥信号量、递归互斥信号量。
二值信号量
二值信号量通常用于互斥访问或同步,二值信号量和互斥信号量非常类似,但是还是有一些差别。互斥信号量拥有优先级继承机制。因此二值信号量更适合用于同步,互斥信号量适用于互斥访问。
二值信号量其实就是一个只有一个队列项的队列,这个特殊的队列要么是满的,要么是空的,这不正好就是二值吗。不在乎队列里村的是什么内容,只在乎这个队列是满的还是空的。
信号量相关函数都用宏定义了。和老的freertos版本相比,没有系统函数了。上面说过二值信号量其实就是没有列表项的队列。
所以都是使用队列的函数的。类型用的是queueQUEUE_TYPE_BINARY_SEMAPHORE。
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
在分析queue里时候也说过,queue有很多类型。
因为在分析queue的时候相关函数已经分析过了,因此这里就值只分析信号量相关的了。
队列长度是1,队列项长度是0。其他和queue一样,不再分析了。
计数信号量
二值信号量就是长度为1的队列,那么计数信号量就是长度大于1的队列。其他没有区别。可以指定信号量的最大值和初始值。
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
就是xQueueCreateCountingSemaphore()函数。
xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );
类型是queueQUEUE_TYPE_COUNTING_SEMAPHORE。 其他内容同queue,不再分析了。
优先级反转
所谓的优先级反转就是指任务没有按照设定的优先级运行,低优先级的任务获得了运行,高优先级阻塞。
出现这种现象的原因是:
假设有三个任务A、B、C。A的优先级最高,C最小。
C访问共享资源X,这时A也访问X,由于X被C访问,所以A这时阻塞。这时B任务得到允许,由于B优先级比C高,那么B得到允许,等B运行完后C继续运行,最后A才得到运行。那么B优先级比A低,反而获得了执行权,这就是优先级反转。
针对这种情况就引入了互斥信号量
互斥信号量
互斥信号量和二值信号量相比,具有优先级继承特性。还是以A、B、C举例。当A也想访问X时,他发现C任务锁住了信号量,这时A就会将C的优先级提高到和A一样的级别,这就是优先级继承。那么这时C一直在运行,A阻塞。假设这时B任务唤醒,由于C的优先级已经提到和A一样了,那么B任务得不到运行。等C结束后,任务A得到运行,A结束后B得到运行。这也就一定程度上防止任务A在B之后运行了。所以只能缓解优先级反转带来的影响,不能完全消除。
互斥信号量不能用在中断中:
- 互斥信号量有优先级继承,只能在任务中使用,不能在中断中使用。
- 中断函数不能应为要等到互斥信号量而进入等到阻塞中。
创建函数:类型queueQUEUE_TYPE_MUTEX。
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
释放信号量时,在prvCopyDataToQueue函数里,
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
/* The mutex is no longer being held. */
xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder );
pxQueue->u.xSemaphore.xMutexHolder = NULL;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */
如果优先级不相等,就会新的优先级放到就绪列表里。
if( pxTCB->uxPriority != pxTCB->uxBasePriority )
{
/* Only disinherit if no other mutexes are held. */
if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )
{
/* A task can only have an inherited priority if it holds
* the mutex. If the mutex is held by a task then it cannot be
* given from an interrupt, and if a mutex is given by the
* holding task then it must be the running state task. Remove
* the holding task from the ready list. */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Disinherit the priority before adding the task into the
* new ready list. */
traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
pxTCB->uxPriority = pxTCB->uxBasePriority;
因为释放信号量表示任务已经处理完毕了,需要将优先级降低到基础优先级,重新添加到就绪列表里。uxBasePriority就是基础优先级。并将基础优先级重新赋值给uxPriority。这也任务的优先级就恢复了。
获取信号量时,xTaskPriorityInherit。如果当前任务优先级比获取资源的任务优先级高,那么就将低优先级任务从就绪表中删除,将低优先级任务优先级设置成高优先级,添加到就绪表里,这样低优先级任务就能能暂时以高优先级运行了。
if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority )
{
/* Adjust the mutex holder state to account for its new
* priority. Only reset the event list item value if the value is
* not being used for anything else. */
if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
{
listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* If the task being modified is in the ready state it will need
* to be moved into a new list. */
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE )
{
if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* It is known that the task is in its ready list so
* there is no need to check again and the port level
* reset macro can be called directly. */
portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Inherit the priority before being moved into the new list. */
pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
prvAddTaskToReadyList( pxMutexHolderTCB );
#if ( configNUMBER_OF_CORES > 1 )
{
/* The priority of the task is raised. Yield for this task
* if it is not running. */
if( taskTASK_IS_RUNNING( pxMutexHolderTCB ) != pdTRUE )
{
prvYieldForTask( pxMutexHolderTCB );
}
}
#endif /* if ( configNUMBER_OF_CORES > 1 ) */
}
调用函数 vTaskPrioritylnheritO)处理互斥信号量中的优先级继承问题,如果函数xQueueGenericReceive()用于获取互斥信号量的话,此函数执行到这里说明互斥信号量正在被其他的任务占用。此函数和xTaskPriorityDisinherit()过程相反。此函数会判断当前任务的任务优先级是否比正在拥有互斥信号量的那个任务的任务优先级高,如果是的话就会把拥有互斥信号量的那个低优先级任务的优先级调整为与当前任务相同的优先级!