• 6. FreeRTOS任务调度


    FreeRTOS任务调度

    • 架构:Cortex-M3

    • 版本:FreeRTOS V9.0.0

    • 前言:上一篇我们分析了任务的切换,其中写到了在vTaskSwitchContext里面的taskSELECT_HIGHEST_PRIORITY_TASK,计算uxTopReadyPriority 的前导零值,那么本篇尝试分析,FreeRTOS依据什么机制来操作uxTopReadyPriority 的。

    1.任务创建

    任务创建时,会把任务优先级的对应位置位到uxTopReadyPriority里,具体函数在prvAddTaskToReadyList

    #define prvAddTaskToReadyList( pxTCB )																
    	traceMOVED_TASK_TO_READY_STATE( pxTCB );														
    	taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority );												
    	vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); 
    	tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB )
    

    比如我们创建第一个任务,任务优先级为1,那么在任务创建完成时,uxTopReadyPriority会被置为0x00000002。

    2.SysTick异常

    ​ FreeRTOS中有一个系统滴答定时器,它会根据宏configTICK_RATE_HZ来设置周期中断时间,比如设置为1000,则1ms发生一次中断,在前面章节分析过,这个中断的优先级是最低的。下面具体看这个中断程序xPortSysTickHandler

    void xPortSysTickHandler( void )
    {
    	/* The SysTick runs at the lowest interrupt priority, so when this interrupt
    	executes all interrupts must be unmasked.  There is therefore no need to
    	save and then restore the interrupt mask value as its value is already
    	known - therefore the slightly faster vPortRaiseBASEPRI() function is used
    	in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
    	vPortRaiseBASEPRI();
    	{
    		/* Increment the RTOS tick. */
    		if( xTaskIncrementTick() != pdFALSE )
    		{
    			/* A context switch is required.  Context switching is performed in
    			the PendSV interrupt.  Pend the PendSV interrupt. */
    			portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
    		}
    	}
    	vPortClearBASEPRIFromISR();
    }
    

    portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT这个是用来调用PendSV的,PendSV上一篇已经详细分析了,来看

    xTaskIncrementTick这个函数:

    BaseType_t xTaskIncrementTick( void )
    {
    TCB_t * pxTCB;
    TickType_t xItemValue;
    BaseType_t xSwitchRequired = pdFALSE;
    
    	/* Called by the portable layer each time a tick interrupt occurs.
    	Increments the tick then checks to see if the new tick value will cause any
    	tasks to be unblocked. */
    	traceTASK_INCREMENT_TICK( xTickCount );
    	if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
    	{
    		/* Minor optimisation.  The tick count cannot change in this
    		block. */
    		const TickType_t xConstTickCount = xTickCount + 1;
    
    		/* Increment the RTOS tick, switching the delayed and overflowed
    		delayed lists if it wraps to 0. */
    		xTickCount = xConstTickCount;
    
    		if( xConstTickCount == ( TickType_t ) 0U )
    		{
    			taskSWITCH_DELAYED_LISTS();
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    
    		/* See if this tick has made a timeout expire.  Tasks are stored in
    		the	queue in the order of their wake time - meaning once one task
    		has been found whose block time has not expired there is no need to
    		look any further down the list. */
    		if( xConstTickCount >= xNextTaskUnblockTime )
    		{
    			for( ;; )
    			{
    				if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
    				{
    					/* The delayed list is empty.  Set xNextTaskUnblockTime
    					to the maximum possible value so it is extremely
    					unlikely that the
    					if( xTickCount >= xNextTaskUnblockTime ) test will pass
    					next time through. */
    					xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
    					break;
    				}
    				else
    				{
    					/* The delayed list is not empty, get the value of the
    					item at the head of the delayed list.  This is the time
    					at which the task at the head of the delayed list must
    					be removed from the Blocked state. */
    					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
    					xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
    
    					if( xConstTickCount < xItemValue )
    					{
    						/* It is not time to unblock this item yet, but the
    						item value is the time at which the task at the head
    						of the blocked list must be removed from the Blocked
    						state -	so record the item value in
    						xNextTaskUnblockTime. */
    						xNextTaskUnblockTime = xItemValue;
    						break;
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    
    					/* It is time to remove the item from the Blocked state. */
    					( void ) uxListRemove( &( pxTCB->xStateListItem ) );
    
    					/* Is the task waiting on an event also?  If so remove
    					it from the event list. */
    					if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
    					{
    						( void ) uxListRemove( &( pxTCB->xEventListItem ) );
    					}
    					else
    					{
    						mtCOVERAGE_TEST_MARKER();
    					}
    
    					/* Place the unblocked task into the appropriate ready
    					list. */
    					prvAddTaskToReadyList( pxTCB );
    
    					/* A task being unblocked cannot cause an immediate
    					context switch if preemption is turned off. */
    					#if (  configUSE_PREEMPTION == 1 )
    					{
    						/* Preemption is on, but a context switch should
    						only be performed if the unblocked task has a
    						priority that is equal to or higher than the
    						currently executing task. */
    						if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
    						{
    							xSwitchRequired = pdTRUE;
    						}
    						else
    						{
    							mtCOVERAGE_TEST_MARKER();
    						}
    					}
    					#endif /* configUSE_PREEMPTION */
    				}
    			}
    		}
    
    		/* Tasks of equal priority to the currently running task will share
    		processing time (time slice) if preemption is on, and the application
    		writer has not explicitly turned time slicing off. */
    		#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
    		{
    			if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
    			{
    				xSwitchRequired = pdTRUE;
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		#endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */
    
    		#if ( configUSE_TICK_HOOK == 1 )
    		{
    			/* Guard against the tick hook being called when the pended tick
    			count is being unwound (when the scheduler is being unlocked). */
    			if( uxPendedTicks == ( UBaseType_t ) 0U )
    			{
    				vApplicationTickHook();
    			}
    			else
    			{
    				mtCOVERAGE_TEST_MARKER();
    			}
    		}
    		#endif /* configUSE_TICK_HOOK */
    	}
    	else
    	{
    		++uxPendedTicks;
    
    		/* The tick hook gets called at regular intervals, even if the
    		scheduler is locked. */
    		#if ( configUSE_TICK_HOOK == 1 )
    		{
    			vApplicationTickHook();
    		}
    		#endif
    	}
    
    	#if ( configUSE_PREEMPTION == 1 )
    	{
    		if( xYieldPending != pdFALSE )
    		{
    			xSwitchRequired = pdTRUE;
    		}
    		else
    		{
    			mtCOVERAGE_TEST_MARKER();
    		}
    	}
    	#endif /* configUSE_PREEMPTION */
    
    	return xSwitchRequired;
    }
    

    ​ 1. 首先判断调度器是否被挂起了,如果没挂起,xTickCount表示一共发生了多少次systick中断,每次进来加1。

    ​ 2. 检查xNextTaskUnblockTime有没有任务使用了延时函数而阻塞的,如果有并且到了时间,就会把阻塞的任务TCB从阻塞链表中移除,并使用prvAddTaskToReadyList把任务通过尾插的方式添加到Ready_List,然后设置下一个因延时函数而阻塞的任务的xNextTaskUnblockTime,这里面就会把该任务对应的优先级链表置位uxTopReadyPriority

    ​ 3. 会使用listCURRENT_LIST_LENGTH来进行判断当前优先级是否只有一个任务,如果只有一个任务就不需要取调度,CPU永远是属于这个任务的。

    ​ 4. 最后调用portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT来启动PendSV

  • 相关阅读:
    python (八)迭代器、生成器、列表推导式
    python (七)装饰器
    HTML基础 (一)
    jQuery(一)
    JavaScript 练习(二)事件
    DOM节点(二)
    git操作顺序
    VUE练习(二)解决Bug
    前端环境VSCode
    JavaScript 基础基础最基础
  • 原文地址:https://www.cnblogs.com/r1chie/p/14141565.html
Copyright © 2020-2023  润新知