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

举报
yd_274589494 发表于 2023/11/27 13:31:05 2023/11/27
【摘要】 @TOC 前言本篇文章将为大家讲解信号量,源码分析。在 FreeRTOS 中,信号量的实现基于队列。这种设计的思想是利用队列的特性来实现信号量,因为信号量可以被视为只能存储 0 或 1 个元素的特殊队列。在 FreeRTOS 中,二进制信号量(Binary Semaphore)通常由一个队列和一个计数器组成。计数信号量允许计数器的值大于 1,它通常用于管理多个相同资源的可用性。计数信号量的值...

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

总结

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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。