FreeRTOS源码理解(八)——信号量

FreeRTOS源码理解(一)——FreeRTOS思路梳理

FreeRTOS源码理解(二)——中断和列表项

FreeRTOS源码理解(三)——任务

FreeRTOS源码理解(四)——任务调度器

FreeRTOS源码理解(五)——任务切换

FreeRTOS源码理解(六)——时间管理

FreeRTOS源码理解(七)——队列

FreeRTOS源码理解(八)——信号量

FreeRTOS源码理解(九)——事件组和任务通知

FreeRTOS源码理解(十)——软件定时器和空闲任务

FreeRTOS源码理解(十一)——内存管理

FreeRTOS源码理解(十二)——汇总

信号量

信号量的本质为队列,各种信号量的底层均是用队列实现的

二值信号量

二值信号量常用于互斥访问或同步,二值信号量和互斥信号量非常像,但是互斥信号量有优先级继承机制,二值信号量没有优先级继承机制,所以二值信号量可能会导致优先级翻转,因为这一问题,二值信号量更适合用于同步,而互斥信号量适合用于简单的互斥访问。

二值信号量底层就是一个长度为1的队列,获取释放等操作和队列的操作相同,只是进行了封装

#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )

计数信号量

计数信号量本质是个长度为uxMaxCount,队列项大小为0的队列;对它的操作和普通的发送消息的队列相似只是做了封装

#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )

QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount )
{
    QueueHandle_t xHandle;

    configASSERT( uxMaxCount != 0 );
    configASSERT( uxInitialCount <= uxMaxCount );

    /* 创建一个队列 queueSEMAPHORE_QUEUE_ITEM_LENGTH==0 */
    xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );

    if( xHandle != NULL )
    {
    ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;

    traceCREATE_COUNTING_SEMAPHORE();
    }
    else
    {
    traceCREATE_COUNTING_SEMAPHORE_FAILED();
    }

    return xHandle;
}

互斥信号量

互斥信号量和二值信号量相似,只是它有优先级继承的特性。当一个互斥信号量被一个低优先级的任务使用时,而此时有个高优先级的任务也在尝试获取这个互斥信号量,这个高优先级的任务将被阻塞。不过,这个高优先级任务会将低优先级任务的优先级提高到和自己一样,这就是优先级继承。优先级继承尽可能得降低了高优先级任务的阻塞时间,并且降低优先级翻转的影响。

互斥信号量本质是一个长度为1,队列项大小为0,队列类型(ucQueueType)为互斥量队列(queueQUEUE_TYPE_MUTEX)的队列。

相比于普通的队列,对互斥信号量的操作多了:

  • 更改TCB中记录获取到的互斥信号量数量
  • 更新队列(即互斥信号量)的所属任务句柄
  • 处理优先级继承

互斥信号量的创建

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

优先级继承

释放互斥信号量中的优先级继承 xTaskPriorityDisinherit

和申请互斥信号量时的操作相反

申请互斥信号量中的优先级继承 vTaskPriorityInherit
void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
{
    TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;

    /* 队列锁定时,中断释放了互斥锁;则互斥锁持有者为NULL */
    if( pxMutexHolder != NULL )
    {
        /* 如果互斥锁持有者优先级低于当前尝试获取互斥锁的任务优先级,则进行优先级翻转 */
        if( pxTCB->uxPriority < pxCurrentTCB->uxPriority )
        {
            /* 判断互斥锁的持有者的事件列表项 */
            if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
            {
                /* 没有在等待事件,将持有者事件列表项的优先级赋为当前尝试获取互斥锁任务的优先级 */
                listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); 
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }

            /* 判断持有者是否处于就绪列表中 */
            if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
            {
                /* 在就绪列表中的话,移出任务,更改其优先级,更新就绪列表的最大优先级,移动任务到新的就绪列表中 */
                if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
                {
                    taskRESET_READY_PRIORITY( pxTCB->uxPriority );
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }

                pxTCB->uxPriority = pxCurrentTCB->uxPriority;
                prvAddTaskToReadyList( pxTCB );
            }
            else
            {
                /* 未在就绪列表的话,只更改优先级 */
                pxTCB->uxPriority = pxCurrentTCB->uxPriority;
            }

            traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority );
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    else
    {
        mtCOVERAGE_TEST_MARKER();
    }
}

递归互斥信号量

它是一种特殊的互斥信号量,一个任务可以递归得获取它,可以获取的次数不限,但是要求任务释放此信号量的次数和获取它的次数相同

递归互斥量的创建

递归互斥量创建过程和互斥量相同

#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )

递归互斥量的释放

#define xSemaphoreGiveRecursive( xMutex )	xQueueGiveMutexRecursive( ( xMutex ) )

BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
    BaseType_t xReturn;
    Queue_t * const pxMutex = ( Queue_t * ) xMutex;

    configASSERT( pxMutex );

    /* 判断,确保当前释放任务和递归互斥锁的持有者是一个任务 */
    if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) 
    {
        traceGIVE_MUTEX_RECURSIVE( pxMutex );

        /* u.uxRecursiveCallCount是用来记录递归互斥锁的获取次数的 */
        ( pxMutex->u.uxRecursiveCallCount )--;

        /* 变量为0说明最后一次释放 */
        if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 )
        {
            /* 往队列发送空数据,唤醒其他等待任务 */
            ( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }

        xReturn = pdPASS;
    }
    else
    {
        /* 如果当前释放锁的任务和锁的持有者不一致,报错 */
        xReturn = pdFAIL;

        traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );
    }

    return xReturn;
}

递归互斥量的获取

#define xSemaphoreTakeRecursive( xMutex, xBlockTime )	xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )

BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait )
{
    BaseType_t xReturn;
    Queue_t * const pxMutex = ( Queue_t * ) xMutex;

    configASSERT( pxMutex );

    traceTAKE_MUTEX_RECURSIVE( pxMutex );

    /* 判断锁的持有者和当前获取锁的任务是否为同一任务 */
    if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) 
    {
        /* 是同一任务,记录锁获取次数的变量加一 */
        ( pxMutex->u.uxRecursiveCallCount )++;
        xReturn = pdPASS;
    }
    else
    {
        /* 尝试获取锁,如果锁未被其他任务持有且为第一次获取锁则获取成功;否则函数返回pdFAIL */
        xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );

        if( xReturn != pdFAIL )
        {
            /* 获取成功,计数加一 */
            ( pxMutex->u.uxRecursiveCallCount )++;
        }
        else
        {
            traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
        }
    }

    return xReturn;
}

文档汇总

文档汇总

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值