前言
本篇文章将为大家讲解信号量,源码分析。
在 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; }
总结
本篇文章主要讲解了信号量,互斥量源码分析,其实信号量,互斥量是一个特殊的队列,掌握了队列后来学习信号量和互斥量的话那就是比较轻松的了。