FreeRTOS深入教程(信号量源码分析)
@TOC
前言
本篇文章将为大家讲解信号量,源码分析。
在 FreeRTOS 中,信号量的实现基于队列。这种设计的思想是利用队列的特性来实现信号量,因为信号量可以被视为只能存储 0 或 1 个元素的特殊队列。
在 FreeRTOS 中,二进制信号量(Binary Semaphore)通常由一个队列和一个计数器组成。
计数信号量允许计数器的值大于 1,它通常用于管理多个相同资源的可用性。计数信号量的值表示可用资源的数量,多个任务可以同时获取不同数量的资源。当任务获取资源时,计数器减少,当任务释放资源时,计数器增加。
一.创建信号量
创建二进制信号量
xBinarySemaphore = xSemaphoreCreateBinary();
创建信号量源码分析:
创建信号量时本质上还是使用到了xQueueGenericCreate队列相关的函数
信号量不能用来进行数据的传输,所以在创建信号量时Itemsize被设置为了0。
将pucQueueStorage指针指向队列的存储区域位置。
初始化队列:
根据uxItemSize是否为0调整pcHead的指向。
对uxLength和uxItemSize进行赋值。
创建计数型信号量:
创建计数型信号量使用到了xQueueCreateCountingSemaphore函数。
使用这个函数时需要开启configUSE_COUNTING_SEMAPHORES和configSUPPORT_DYNAMIC_ALLOCATION这两个宏。
创建计数型信号量时还是会使用到xQueueGenericCreate函数进行创建。
唯一的区别就是二值信号量的uxMessagesWaiting参数只能是0或者1,而计数型信号量的uxMessagesWaiting可以为任意的值(0和正数)。
二.释放信号量
由于释放信号量会涉及到访问共享资源,所以开始先关闭中断。
taskENTER_CRITICAL();
判断pxQueue->uxMessagesWaiting是否小于pxQueue->uxLength,如果小于,调用prvCopyDataToQueue函数让uxMessagesWaiting的值加1。
if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
{
.........
xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );
}
判断是否有任务在等待信号量,有的话移除并且唤醒任务优先级最高的任务。
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
/* The unblocked task has a priority higher than
* our own so yield immediately. Yes it is ok to do
* this from within the critical section - the kernel
* takes care of that. */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
由于xSemaphoreGive函数不涉及超时参数,故不涉及超时处理。
三.获取信号量
成功获取
由于获取信号量会涉及到访问共享资源,所以开始先关闭中断。
taskENTER_CRITICAL();
获取当前count值
const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;
判断uxSemaphoreCount是否大于0,大于0则uxMessagesWaiting的值减1。
if( uxSemaphoreCount > ( UBaseType_t ) 0 )
{
traceQUEUE_RECEIVE( pxQueue );
/* Semaphores are queues with a data size of zero and where the
* messages waiting is the semaphore's count. Reduce the count. */
pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1;
获取不成功
当获取不成功,并且没有设置超时时间直接返回错误:
if( xTicksToWait == ( TickType_t ) 0 )
{
/* For inheritance to have occurred there must have been an
* initial timeout, and an adjusted timeout cannot become 0, as
* if it were 0 the function would have exited. */
#if ( configUSE_MUTEXES == 1 )
{
configASSERT( xInheritanceOccurred == pdFALSE );
}
#endif /* configUSE_MUTEXES */
/* The semaphore count was 0 and no block time is specified
* (or the block time has expired) so exit now. */
taskEXIT_CRITICAL();
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;//返回错误
}
设置超时时间
else if( xEntryTimeSet == pdFALSE )
{
/* The semaphore count was 0 and a block time was specified
* so configure the timeout structure ready to block. */
vTaskInternalSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE;
}
检查是否超时,并且放入xTasksWaitingToReceive链表中。
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
{
/* A block time is specified and not expired. If the semaphore
* count is 0 then enter the Blocked state to wait for a semaphore to
* become available. As semaphores are implemented with queues the
* queue being empty is equivalent to the semaphore count being 0. */
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
taskENTER_CRITICAL();
{
xInheritanceOccurred = xTaskPriorityInherit( pxQueue->u.xSemaphore.xMutexHolder );
}
taskEXIT_CRITICAL();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* if ( configUSE_MUTEXES == 1 ) */
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
prvUnlockQueue( pxQueue );
超时直接返回错误
else
{
/* Timed out. */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
/* If the semaphore count is 0 exit now as the timeout has
* expired. Otherwise return to attempt to take the semaphore that is
* known to be available. As semaphores are implemented by queues the
* queue being empty is equivalent to the semaphore count being 0. */
if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
{
#if ( configUSE_MUTEXES == 1 )
{
/* xInheritanceOccurred could only have be set if
* pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to
* test the mutex type again to check it is actually a mutex. */
if( xInheritanceOccurred != pdFALSE )
{
taskENTER_CRITICAL();
{
UBaseType_t uxHighestWaitingPriority;
/* This task blocking on the mutex caused another
* task to inherit this task's priority. Now this task
* has timed out the priority should be disinherited
* again, but only as low as the next highest priority
* task that is waiting for the same mutex. */
uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue );
vTaskPriorityDisinheritAfterTimeout( pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority );
}
taskEXIT_CRITICAL();
}
}
#endif /* configUSE_MUTEXES */
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
总结
本篇文章主要讲解了信号量,互斥量源码分析,其实信号量,互斥量是一个特殊的队列,掌握了队列后来学习信号量和互斥量的话那就是比较轻松的了。
- 点赞
- 收藏
- 关注作者
评论(0)