FreeRTOS的信号量之互斥锁的阻塞实现xTaskCheckForTimeOut

vTaskPlaceOnEventList

接收函数xQueueGenericReceive里面是一个for(;;)死循环的,所以任务就出不来,看着是阻塞了,并不是和vTaskDelay函数的做法那样将这个任务加到延时列表中的阻塞,而是该任务还是会不停的轮询判断事件的到来,只是在for(;;)中出不来而已;在tick中断里检查时间是否到,而是不停for(;;)的去检查阻塞时间,时间到了又回到for(;;)开始去读队列内容。

互斥锁的本质是信号量,FreeRTOS 信号量包括二进制信号量、计数信号量、互斥锁和递归互斥锁。

与二进制信号量最大的不同在于, 互斥信号量带有优先级继承的机制,这个机制用于减低优先级反转的影响。

#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

从创建函数看,互斥锁是通过队列来实现的,队列的深度为1.


	xQueueHandle xQueueCreateMutex( unsigned char ucQueueType )
	{
	xQUEUE *pxNewQueue;

		/* Prevent compiler warnings about unused parameters if
		configUSE_TRACE_FACILITY does not equal 1. */
		( void ) ucQueueType;

		/* Allocate the new queue structure. */
		pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) );
		if( pxNewQueue != NULL )
		{
			/* Information required for priority inheritance. */
			pxNewQueue->pxMutexHolder = NULL;
			pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;		
			/* Ensure the event queues start with the correct state. */
			vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) );
			vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) );
			traceCREATE_MUTEX( pxNewQueue );
			/* Start with the semaphore in the expected state. */
			xQueueGenericSend( pxNewQueue, NULL, ( portTickType ) 0U, queueSEND_TO_BACK );
		}
		configASSERT( pxNewQueue );
		return pxNewQueue;
	}

拿锁

任务通过xSemaphoreTake来拿锁

#define xSemaphoreTake( xSemaphore, xBlockTime )    \
    xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE )

在一个任务拿锁后, 其他任务尝试拿锁失败,如果设置了阻塞时间,则该任务会被阻塞,在进入阻塞前, 函数会判断当前任务的优先级是否高于拥有锁任务的优先级,如果高于, 则会先提高拥有锁任务的优先级。

互斥锁不能在中断使用, 因为中断函数没有优先级继承,同时, 中断函数不能阻塞。

BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
int8_t *pcOriginalReadPosition;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;

    configASSERT( pxQueue );
    configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) );
    #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();
        {
            const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;  //队列中item个数

            /* 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( uxMessagesWaiting > ( UBaseType_t ) 0 )  //队列中有item
            {
                /* Remember the read position in case the queue is only being
                peeked. */
                pcOriginalReadPosition = pxQueue->u.pcReadFrom;  //记录队列的读指针

                prvCopyDataFromQueue( pxQueue, pvBuffer );       //<-- 从队列拷贝出数据   ##3 见后

                if( xJustPeeking == pdFALSE )  【不是peek的方法】
                {
                    traceQUEUE_RECEIVE( pxQueue );

                    /* Actually removing data, not just peeking. */
                    pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;  //<-- 队列中item个数 减一

                    #if ( configUSE_MUTEXES == 1 )  //不关注是否是mutex 
                    #endif /* configUSE_MUTEXES */

                        //有任务在等待发送消息,此时队列有空间了,可以发送
                    if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
                    {
                        if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
                        {
                            queueYIELD_IF_USING_PREEMPTION();  //置位PendSV
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
                else  【peek】
                {
                    traceQUEUE_PEEK( pxQueue );

                    /* The data is not being removed, so reset the read
                    pointer. */
                    pxQueue->u.pcReadFrom = pcOriginalReadPosition;

                    /* The data is being left in the queue, so see if there are
                    any other tasks waiting for the data. 
                        peek之后的消息仍在队列中,所以看一下是否有任务在等待接收消息*/
                    if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
                    {
                        if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
                        {
                            /* The task waiting has a higher priority than this task. */
                            queueYIELD_IF_USING_PREEMPTION();  //置位PendSV
                        }
                        else
                        {
                            mtCOVERAGE_TEST_MARKER();
                        }
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }/* end of peek? */

                taskEXIT_CRITICAL();
                return pdPASS;
            }
            else  【queue has no data】
            {
                if( xTicksToWait == ( TickType_t ) 0 )
                {
                    /* The queue was empty and no block time is specified (or
                    the block time has expired) so leave now. */
                    taskEXIT_CRITICAL();
                    traceQUEUE_RECEIVE_FAILED( pxQueue );
                    return errQUEUE_EMPTY;  //直接返回err
                }
                else if( xEntryTimeSet == pdFALSE )
                {
                    /* The queue was empty and a block time was specified so
                    configure the timeout structure. */
                    vTaskSetTimeOutState( &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( prvIsQueueEmpty( pxQueue ) != pdFALSE ) 【队列是空的】
            {
                traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );

                #if ( configUSE_MUTEXES == 1 )
                #endif

                    /* 将任务添加到队列的 xTasksWaitingToRCV 列表中 和 延时列表中,
                       并且将任务从就绪列表中移除。##0 见后*/
                vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
                prvUnlockQueue( pxQueue );
                if( xTaskResumeAll() == pdFALSE )
                {
                    portYIELD_WITHIN_API();  
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else 【队列不是空的】
            {
                /* Try again. 从for(;;)那里重新来一遍*/
                prvUnlockQueue( pxQueue );
                ( void ) xTaskResumeAll();
            }
        }
        else  【 timeout 】
        {
            prvUnlockQueue( pxQueue );  //队列解锁
            ( void ) xTaskResumeAll();  //恢复任务调度

            if( prvIsQueueEmpty( pxQueue ) != pdFALSE )  //队列仍是空的
            {
                traceQUEUE_RECEIVE_FAILED( pxQueue );
                return errQUEUE_EMPTY;  //返回错误
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
    }
}
/*-----------------------------------------------------------*/

xTaskCheckForTimeOut的实现:
每次任务醒来时就调用xTaskCheckForTimeOut检查延时是否到了,在xTaskCheckForTimeOut函数中,用当前tick-当时标记的tick,结果大于0说明时间还有,延时还没有到,延时没到就继续用vTaskSetTimeOutState来调整更新新的剩余延时时间

xTaskIncrementTick调用taskSWITCH_DELAYED_LISTS
节拍计数器xTickCount溢出就对xNumOfOverflows加一

/*-----------------------------------------------------------*/

BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait )
{
BaseType_t xReturn;

	configASSERT( pxTimeOut );
	configASSERT( pxTicksToWait );

	taskENTER_CRITICAL();
	{
		/* Minor optimisation.  The tick count cannot change in this block. */
		const TickType_t xConstTickCount = xTickCount;

		#if( INCLUDE_xTaskAbortDelay == 1 )
			if( pxCurrentTCB->ucDelayAborted != pdFALSE )
			{
				/* The delay was aborted, which is not the same as a time out,
				but has the same result. */
				pxCurrentTCB->ucDelayAborted = pdFALSE;
				xReturn = pdTRUE;
			}
			else
		#endif

		#if ( INCLUDE_vTaskSuspend == 1 )
			if( *pxTicksToWait == portMAX_DELAY )
			{
				/* If INCLUDE_vTaskSuspend is set to 1 and the block time
				specified is the maximum block time then the task should block
				indefinitely, and therefore never time out. */
				xReturn = pdFALSE;
			}
			else
		#endif

		if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */
		{
			/* The tick count is greater than the time at which
			vTaskSetTimeout() was called, but has also overflowed since
			vTaskSetTimeOut() was called.  It must have wrapped all the way
			around and gone past again. This passed since vTaskSetTimeout()
			was called. */
			xReturn = pdTRUE;
		}
		else if( ( ( TickType_t ) ( xConstTickCount - pxTimeOut->xTimeOnEntering ) ) < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */
		当前tick-当时标记的tick大于0说明时间还有,延时还没有到
		{
			/* Not a genuine timeout. Adjust parameters for time remaining. */
			在这里不断的调整剩余时间,tick-当时标记的tick作为新的延时时间重新写入作为标记
			*pxTicksToWait -= ( xConstTickCount - pxTimeOut->xTimeOnEntering );
			vTaskSetTimeOutState( pxTimeOut );
			xReturn = pdFALSE;
		}
		else
		{
			xReturn = pdTRUE;
		}
	}
	taskEXIT_CRITICAL();

	return xReturn;
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值