Freertos代码之互斥信号量

信号量用于限制对共享资源的访问和多任务之间的同步。三个信号量API函数都是宏,使用现有的队列实现。

使用例子:

typedef void * QueueHandle_t;
typedef QueueHandle_t SemaphoreHandle_t;

SemaphoreHandle_t mutex_sema;
mutex_sema = xSemaphoreCreateMutex();

 

xSemaphoreTake(mutex_sema, portMAX_DELAY);

/* 对共享资源的操作 */

xSemaphoreGive(mutex_sema);

 

代码:

/*

 * xSemaphoreCreateMutex

 */

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

    QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
    {
    Queue_t *pxNewQueue;
    const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;

        pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); // 创建队列,注意参数.
        prvInitialiseMutex( pxNewQueue );  // 初始化刚创建的普通队列为互斥体

        return pxNewQueue;
    }

    static void prvInitialiseMutex( Queue_t *pxNewQueue )
    {
        if( pxNewQueue != NULL )
        {
            /* The queue create function will set all the queue structure members
            correctly for a generic queue, but this function is creating a
            mutex.  Overwrite those members that need to be set differently -
            in particular the information required for priority inheritance. */
            /* 队列创建函数将为通用队列正确设置所有队列结构成员,但此函数正在创建互斥体。
               覆盖那些需要以不同方式设置的成员-特别是优先级继承所需的信息 */

            // #define pxMutexHolder                pcTail
            // #define uxQueueType                    pcHead

            pxNewQueue->pxMutexHolder = NULL;
            pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;

            /* In case this is a recursive mutex. */
            pxNewQueue->u.uxRecursiveCallCount = 0;

            traceCREATE_MUTEX( pxNewQueue );

            /* Start with the semaphore in the expected state. */
            /* 从处于预期状态的信号量开始 */
            ( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
        }
        else
        {
            traceCREATE_MUTEX_FAILED();
        }
    }

 

/*

 * xSemaphoreTake

 */

#define xSemaphoreTake( xSemaphore, xBlockTime )        xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )

BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

#if( configUSE_MUTEXES == 1 )
    BaseType_t xInheritanceOccurred = pdFALSE;
#endif

    /* Check the queue pointer is not NULL. */
    configASSERT( ( pxQueue ) );

    /* Check this really is a semaphore, in which case the item size will be
    0. 检查这是否是一个信号量,在这种情况下,项目大小将为0 */
    configASSERT( pxQueue->uxItemSize == 0 );

    /* Cannot block if the scheduler is suspended. 如果计划程序已挂起,则无法阻止 */
    #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
    {
        configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
    }
    #endif


    /* This function relaxes the coding standard somewhat to allow return
    statements within the function itself.  This is done in the interest
    of execution time efficiency. */
    /* 这个函数在一定程度上放宽了编码标准,允许函数本身内部有返回语句。这是为了提高执行时间效率 */

    for( ;; )
    {
        taskENTER_CRITICAL();
        {
            /* Semaphores are queues with an item size of 0, and where the
            number of messages in the queue is the semaphore's count value. */
            /* 信号量是项目大小为0的队列,其中队列中的消息数是信号量的计数值 */
            const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;

            /* Is there data in the queue now?  To be running the calling task
            must be the highest priority task wanting to access the queue. */
            /* 队列中现在有数据吗?要运行调用任务,必须是要访问队列的最高优先级任务 */
            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 ( configUSE_MUTEXES == 1 )
                {
                    if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
                    {
                        /* Record the information required to implement
                        priority inheritance should it become necessary. */
                        /* 如果有必要,记录实现优先级继承所需的信息 */
                        pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount(); /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                #endif /* configUSE_MUTEXES */

                /* Check to see if other tasks are blocked waiting to give the
                semaphore, and if so, unblock the highest priority such task. */
                /* 检查是否有其他任务在等待发出信号量时被阻塞,如果是,请取消阻止优先级最高的任务 */
                if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
                {
                    if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
                    {
                        queueYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                taskEXIT_CRITICAL();
                return pdPASS;
            }
            else
            {
                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. */
                    /* 要使继承发生,必须有一个初始超时,并且调整后的超时不能变为0,如果它为0,函数将退出 */
                    #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. */
                    /* 信号量计数为0,并且未指定块时间(或块时间已过期),因此请立即退出 */
                    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. */
                    /* 信号量计数为0,并且指定了阻塞时间,因此请配置超时结构以准备阻塞 */
                    vTaskInternalSetTimeOutState( &xTimeOut );
                    xEntryTimeSet = pdTRUE;
                }
                else
                {
                    /* Entry time was already set. */
                    /* 已设置进入时间 */
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        taskEXIT_CRITICAL();

        /* Interrupts and other tasks can give to and take from the semaphore
        now the critical section has been exited. */
        /* 中断和其他任务可以对信号量进行交换,现在关键部分已经退出 */

        vTaskSuspendAll();
        prvLockQueue( pxQueue );

        /* Update the timeout state to see if it has expired yet. */
        /* 更新超时状态以查看它是否已过期 */
        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. */
            /* 块时间已指定且未过期。如果信号量计数为0,则进入阻塞状态,等待信号量变为可用。
               由于信号量是用队列实现的,因此队列为空相当于信号量计数为0 */
            if( prvIsQueueEmpty( pxQueue ) != pdFALSE )
            {
                traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );

                #if ( configUSE_MUTEXES == 1 )
                {
                    if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
                    {
                        taskENTER_CRITICAL();
                        {
                            xInheritanceOccurred = xTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
                        }
                        taskEXIT_CRITICAL();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                #endif

                vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
                prvUnlockQueue( pxQueue );
                if( xTaskResumeAll() == pdFALSE )
                {
                    portYIELD_WITHIN_API();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                /* There was no timeout and the semaphore count was not 0, so
                attempt to take the semaphore again. */
                /* 没有超时,信号量计数不为0,因此请尝试再次获取该信号量 */
                prvUnlockQueue( pxQueue );
                ( void ) xTaskResumeAll();
            }
        }
        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. */
            /* 如果信号量计数为0,则立即退出,因为超时已过期。否则返回尝试获取已知可用的信号量。
               由于信号量是由队列实现的,因此队列为空就等于信号量计数为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. */
                    /* 只有当pxQueue->uxQueueType==queueQUEUE_IS_MUTEX时,才能设置xinHeritanceOccurrence,
                       因此无需再次测试互斥体类型以检查它是否是互斥体 */
                    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( ( void * ) pxQueue->pxMutexHolder, uxHighestWaitingPriority );
                        }
                        taskEXIT_CRITICAL();
                    }
                }
                #endif /* configUSE_MUTEXES */

                traceQUEUE_RECEIVE_FAILED( pxQueue );
                return errQUEUE_EMPTY;
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }
}

/*

 * xSemaphoreGive

 */

#define xSemaphoreGive( xSemaphore )        xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )

BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition )
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

    configASSERT( pxQueue );
    configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
    configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) );
    #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
    {
        configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
    }
    #endif


    /* This function relaxes the coding standard somewhat to allow return
    statements within the function itself.  This is done in the interest
    of execution time efficiency. */
    /* 这个函数在一定程度上放宽了编码标准,允许函数本身内部有返回语句。这是为了提高执行时间效率 */
    for( ;; )
    {
        taskENTER_CRITICAL();
        {
            /* Is there room on the queue now?  The running task must be the
            highest priority task wanting to access the queue.  If the head item
            in the queue is to be overwritten then it does not matter if the
            queue is full. */
            /* 现在排队还有空吗?正在运行的任务必须是要访问队列的最高优先级任务。如果要覆盖队列中的头项,那么队列是否已满并不重要 */
            if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) )
            {
                traceQUEUE_SEND( pxQueue );
                xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition );

                #if ( configUSE_QUEUE_SETS == 1 )
                {
                    if( pxQueue->pxQueueSetContainer != NULL )
                    {
                        if( prvNotifyQueueSetContainer( pxQueue, xCopyPosition ) != pdFALSE )
                        {
                            /* The queue is a member of a queue set, and posting
                            to the queue set caused a higher priority task to
                            unblock. A context switch is required. */
                            /* 队列是队列集的成员,投递到队列集中会导致优先级较高的任务取消阻止。需要上下文切换 */
                            queueYIELD_IF_USING_PREEMPTION();
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                    else
                    {
                        /* If there was a task waiting for data to arrive on the
                        queue then unblock it now. */
                        /* 如果有一个任务正在等待数据到达队列,请立即取消阻止它 */
                        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();
                            }
                        }
                        else if( xYieldRequired != pdFALSE )
                        {
                            /* This path is a special case that will only get
                            executed if the task was holding multiple mutexes
                            and the mutexes were given back in an order that is
                            different to that in which they were taken. */
                            /* 此路径是一种特殊情况,只有当任务持有多个互斥锁,并且互斥锁的返回顺序与它们的获取顺序不同时,才会执行该路径 */
                            queueYIELD_IF_USING_PREEMPTION();
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                }
                #else /* configUSE_QUEUE_SETS */
                {
                    /* If there was a task waiting for data to arrive on the
                    queue then unblock it now. */
                    /* 如果有一个任务正在等待数据到达队列,请立即取消阻止它 */
                    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();
                        }
                    }
                    else if( xYieldRequired != pdFALSE )
                    {
                        /* This path is a special case that will only get
                        executed if the task was holding multiple mutexes and
                        the mutexes were given back in an order that is
                        different to that in which they were taken. */
                        /* 此路径是一种特殊情况,只有当任务持有多个互斥锁,并且互斥锁的返回顺序与它们的获取顺序不同时,才会执行该路径 */
                        queueYIELD_IF_USING_PREEMPTION();
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                #endif /* configUSE_QUEUE_SETS */

                taskEXIT_CRITICAL();
                return pdPASS;
            }
            else
            {
                if( xTicksToWait == ( TickType_t ) 0 )
                {
                    /* The queue was full and no block time is specified (or
                    the block time has expired) so leave now. */
                    /* 队列已满,未指定块时间(或块时间已过期),请立即离开 */
                    taskEXIT_CRITICAL();

                    /* Return to the original privilege level before exiting
                    the function. */
                    /* 在退出函数之前返回到原始权限级别 */
                    traceQUEUE_SEND_FAILED( pxQueue );
                    return errQUEUE_FULL;
                }
                else if( xEntryTimeSet == pdFALSE )
                {
                    /* The queue was full and a block time was specified so
                    configure the timeout structure. */
                    /* 队列已满,并且指定了块时间,因此请配置超时结构 */
                    vTaskInternalSetTimeOutState( &xTimeOut );
                    xEntryTimeSet = pdTRUE;
                }
                else
                {
                    /* Entry time was already set. */
                    /* 已设置进入时间 */
                    mtCOVERAGE_TEST_MARKER();
                }
            }
        }
        taskEXIT_CRITICAL();

        /* Interrupts and other tasks can send to and receive from the queue
        now the critical section has been exited. */
        /* 中断和其他任务可以发送到队列或从队列接收,现在关键部分已经退出 */

        vTaskSuspendAll();
        prvLockQueue( pxQueue );

        /* Update the timeout state to see if it has expired yet. */
        /* 更新超时状态以查看它是否已过期 */
        if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE )
        {
            if( prvIsQueueFull( pxQueue ) != pdFALSE )
            {
                traceBLOCKING_ON_QUEUE_SEND( pxQueue );
                vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait );

                /* Unlocking the queue means queue events can effect the
                event list.  It is possible that interrupts occurring now
                remove this task from the event list again - but as the
                scheduler is suspended the task will go onto the pending
                ready last instead of the actual ready list. */
                /* 解锁队列意味着队列事件可以影响事件列表。现在发生的中断可能会再次将此任务从事件列表中删除-但是由于调度程序被挂起,
                   任务将转到挂起的就绪最后一个,而不是实际的就绪列表 */
                prvUnlockQueue( pxQueue );

                /* Resuming the scheduler will move tasks from the pending
                ready list into the ready list - so it is feasible that this
                task is already in a ready list before it yields - in which
                case the yield will not cause a context switch unless there
                is also a higher priority task in the pending ready list. */
                /* 恢复调度程序会将任务从挂起的就绪列表移动到就绪列表中—因此,此任务在其生成之前已经在就绪列表中是可行的—在这种情况下,
                   除非挂起的就绪列表中还有更高优先级的任务,否则产生的结果不会导致上下文切换 */
                if( xTaskResumeAll() == pdFALSE )
                {
                    portYIELD_WITHIN_API();
                }
            }
            else
            {
                /* Try again. */
                prvUnlockQueue( pxQueue );
                ( void ) xTaskResumeAll();
            }
        }
        else
        {
            /* The timeout has expired. */
            /* 超时已过期 */
            prvUnlockQueue( pxQueue );
            ( void ) xTaskResumeAll();

            traceQUEUE_SEND_FAILED( pxQueue );
            return errQUEUE_FULL;
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值