FreeRTOS深入教程(信号量源码分析)

简介: FreeRTOS深入教程(信号量源码分析)

前言

本篇文章将为大家讲解信号量,源码分析。

在 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;
            }

总结

本篇文章主要讲解了信号量,互斥量源码分析,其实信号量,互斥量是一个特殊的队列,掌握了队列后来学习信号量和互斥量的话那就是比较轻松的了。


相关文章
|
1月前
|
API 调度
【FreeRTOS】互斥锁的使用
【FreeRTOS】互斥锁的使用
|
2月前
|
算法 Unix 调度
【Qt 线程】深入探究QThread线程优先级:原理、应用与最佳实践
【Qt 线程】深入探究QThread线程优先级:原理、应用与最佳实践
36 0
|
1月前
|
API 调度
【FreeRTOS】信号量的使用
【FreeRTOS】信号量的使用
|
5月前
FreeRTOS入门教程(信号量的具体使用)
FreeRTOS入门教程(信号量的具体使用)
45 0
|
5月前
|
消息中间件 算法 调度
FreeRTOS入门教程(同步与互斥)
FreeRTOS入门教程(同步与互斥)
98 0
|
5月前
|
算法 调度
FreeRTOS入门教程(互斥锁的概念和函数使用)
FreeRTOS入门教程(互斥锁的概念和函数使用)
92 0
|
5月前
|
存储 消息中间件 API
FreeRTOS深入教程(软件定时器源码分析)
FreeRTOS深入教程(软件定时器源码分析)
98 0
|
5月前
|
存储 调度
FreeRTOS深入教程(队列内部机制和源码分析)
FreeRTOS深入教程(队列内部机制和源码分析)
67 0
|
5月前
|
存储 安全
FreeRTOS入门教程(队列的概念及相关函数介绍)
FreeRTOS入门教程(队列的概念及相关函数介绍)
48 0
|
5月前
|
存储 缓存 Linux
Linux驱动开发(锁和信号量的概念及实现原理)
Linux驱动开发(锁和信号量的概念及实现原理)
50 0