freeRTOS 开启关闭调度器、挂起恢复调度器、vTaskStepTick

本文详细介绍了FreeRTOS实时操作系统中调度器的启动、挂起及恢复过程。包括开启调度器时的空闲任务创建、中断配置、FPU单元初始化等步骤;调度器挂起与恢复时的任务状态管理和时间补偿机制;以及低功耗模式下时间补偿的具体实现。
部署运行你感兴趣的模型镜像
1. 开启调度器
vTaskStartScheduler | + vPortSetupTimerInterrupt 设置systick,初始化低功耗运行系统补偿时间
+----xPortStartScheduler -- + prvEnableVFP 开启浮点运算单元
+ prvStartFirstTask 开启第一个任务,SVC异常处理函数
2. 关闭调度器,啥都没用。

3. 调度器挂起,挂起层数计数变量。
4. 调度器恢复

5. 低功耗模式系统补偿函数

开启调度器

  1 void vTaskStartScheduler( void )
  2 {
  3 BaseType_t xReturn;
  4 
  5     
  6     /* Add the idle task at the lowest priority. */
  7     #if( configSUPPORT_STATIC_ALLOCATION == 1 )  静态添加空闲任务
  8     {
  9         StaticTask_t *pxIdleTaskTCBBuffer = NULL;
 10         StackType_t *pxIdleTaskStackBuffer = NULL;
 11         uint32_t ulIdleTaskStackSize;
 12 
 13         /* The Idle task is created using user provided RAM - obtain the
 14         address of the RAM then create the idle task. */  添加Idle任务
 15         vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
 16         xIdleTaskHandle = xTaskCreateStatic(    prvIdleTask,
 17                                                 "IDLE",
 18                                                 ulIdleTaskStackSize,
 19                                                 ( void * ) NULL,
 20                                                 ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
 21                                                 pxIdleTaskStackBuffer,
 22                                                 pxIdleTaskTCBBuffer ); /*lint !e961 */
 23 
 24         if( xIdleTaskHandle != NULL )
 25         {
 26             xReturn = pdPASS;
 27         }
 28         else
 29         {
 30             xReturn = pdFAIL;
 31         }
 32     }
 33     #else
 34     {
 35         /* The Idle task is being created using dynamically allocated RAM. */
 36         xReturn = xTaskCreate(    prvIdleTask,
 37                                 "IDLE", configMINIMAL_STACK_SIZE,
 38                                 ( void * ) NULL,
 39                                 ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
 40                                 &xIdleTaskHandle ); /*lint !e961 */
 41     }
 42     #endif /* configSUPPORT_STATIC_ALLOCATION */
 43 
 44     #if ( configUSE_TIMERS == 1 )
 45     {
 46         if( xReturn == pdPASS )
 47         {
 48             xReturn = xTimerCreateTimerTask();  创建定时器服务!后面分析
 49         }
 50         else
 51         {
 52             mtCOVERAGE_TEST_MARKER();
 53         }
 54     }
 55     #endif /* configUSE_TIMERS */
 56 
 57     if( xReturn == pdPASS )
 58     {
 59         /* Interrupts are turned off here, to ensure a tick does not occur
 60         before or during the call to xPortStartScheduler().  The stacks of
 61         the created tasks contain a status word with interrupts switched on
 62         so interrupts will automatically get re-enabled when the first task
 63         starts to run. */
 64         portDISABLE_INTERRUPTS();
 65 
 66         #if ( configUSE_NEWLIB_REENTRANT == 1 )  【略】
 67         {
 68             /* Switch Newlib's _impure_ptr variable to point to the _reent
 69             structure specific to the task that will run first. */
 70             _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
 71         }
 72         #endif /* configUSE_NEWLIB_REENTRANT */
 73 
 74         xNextTaskUnblockTime = portMAX_DELAY;   三个值的初始化
 75         xSchedulerRunning = pdTRUE;
 76         xTickCount = ( TickType_t ) 0U;
 77 
 78         /* If configGENERATE_RUN_TIME_STATS is defined then the following
 79         macro must be defined to configure the timer/counter used to generate
 80         the run time counter time base. */
 81         portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();  时间统计功能若打开,用户实现此宏。
 82 
 83         /* Setting up the timer tick is hardware specific and thus in the
 84         portable interface. */
 85         if( xPortStartScheduler() != pdFALSE )
 86         {
 87             /* Should not reach here as if the scheduler is running the  
 88             function will not return. */  as(因为)
 89         }
 90         else
 91         {
 92             /* Should only reach here if a task calls xTaskEndScheduler(). */  除非(if)
 93         }
 94     }
 95     else
 96     {
 97         /* This line will only be reached if the kernel could not be started,
 98         because there was not enough FreeRTOS heap to create the idle task
 99         or the timer task. */
100         configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY );  idle task 或 timer task 没创建成功
101     }
102 
103     /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0,
104     meaning xIdleTaskHandle is not used anywhere else. */
105     ( void ) xIdleTaskHandle;  防止编译器报错,xTaskGetIdleTaskHandle为0时,编译器提示idle task not use.
106 }
107 /*-----------------------------------------------------------*/
   硬件初始化:systick、FPU单元、PendSV中断。
1 BaseType_t xPortStartScheduler( void ) 2 { 3 /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0. 4 See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ 5 configASSERT( configMAX_SYSCALL_INTERRUPT_PRIORITY ); 6 7 /* This port can be used on all revisions of the Cortex-M7 core other than 8 the r0p1 parts. r0p1 parts should use the port from the 9 /source/portable/GCC/ARM_CM7/r0p1 directory. */ 10 configASSERT( portCPUID != portCORTEX_M7_r0p1_ID ); 11 configASSERT( portCPUID != portCORTEX_M7_r0p0_ID ); 12 13 #if( configASSERT_DEFINED == 1 ) 【略】 14 { 15 volatile uint32_t ulOriginalPriority; 16 volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); 17 volatile uint8_t ucMaxPriorityValue; 18 19 /* Determine the maximum priority from which ISR safe FreeRTOS API 20 functions can be called. ISR safe functions are those that end in 21 "FromISR". FreeRTOS maintains separate thread and ISR API functions to 22 ensure interrupt entry is as fast and simple as possible. 23 24 Save the interrupt priority value that is about to be clobbered. */ 25 ulOriginalPriority = *pucFirstUserPriorityRegister; 26 27 /* Determine the number of priority bits available. First write to all 28 possible bits. */ 29 *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; 30 31 /* Read the value back to see how many bits stuck. */ 32 ucMaxPriorityValue = *pucFirstUserPriorityRegister; 33 34 /* The kernel interrupt priority should be set to the lowest 35 priority. */ 36 configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) ); 37 38 /* Use the same mask on the maximum system call priority. */ 39 ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; 40 41 /* Calculate the maximum acceptable priority group value for the number 42 of bits read back. */ 43 ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS; 44 while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) 45 { 46 ulMaxPRIGROUPValue--; 47 ucMaxPriorityValue <<= ( uint8_t ) 0x01; 48 } 49 50 /* Shift the priority group value back to its position within the AIRCR 51 register. */ 52 ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; 53 ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; 54 55 /* Restore the clobbered interrupt priority register to its original 56 value. */ 57 *pucFirstUserPriorityRegister = ulOriginalPriority; 58 } 59 #endif /* conifgASSERT_DEFINED */ 60 61 /* Make PendSV and SysTick the lowest priority interrupts. */ 62 portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI; 63 portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI; 64 65 /* Start the timer that generates the tick ISR. Interrupts are disabled 66 here already. */ 67 vPortSetupTimerInterrupt(); 68 69 /* Initialise the critical nesting count ready for the first task. */ 70 uxCriticalNesting = 0; 临界区嵌套计数器,防止多个临界区时,其中一个临界区退出,导致多个临界区都退出。 71 72 /* Ensure the VFP is enabled - it should be anyway. */ 73 prvEnableVFP(); 使能VFP 74 75 /* Lazy save always. */ 76 *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; 使S0~S15和FPSCR寄存器,中断时自动保存和恢复。FPCCR寄存器、惰性压栈,参见《权威指南》12章 浮点运算 77 78 /* Start the first task. */ 79 prvStartFirstTask(); 80 81 /* Should not get here! */ 82 return 0; 83 }

一、开启systick

    void vPortSetupTimerInterrupt( void )
    {
        /* Calculate the constants required to configure the tick interrupt. */
        #if configUSE_TICKLESS_IDLE == 1
        {
            ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );  系统时钟频率/sysTick频率
            xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;  //0xFFF_FFF ul
            ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
        }
        #endif /* configUSE_TICKLESS_IDLE */

        /* Configure SysTick to interrupt at the requested rate. */
        portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;                                  设置重装载值
        portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );  开启xx
    }
/* A fiddle欺骗的 factor to estimate the number of SysTick counts that would have
occurred while the SysTick counter is stopped during tickless idle
calculations. */
#define portMISSED_COUNTS_FACTOR    ( 45UL )   详见低功耗运行补偿时间
portNVIC_SYSTICK_CLK_BIT         1<<2
portNVIC_SYSTICK_INT_BIT         1<<1
portNVIC_SYSTICK_ENABLE_BIT      1<<0

#define portNVIC_SYSTICK_CTRL_REG            ( * ( ( volatile uint32_t * ) 0xe000e010 ) )

 

二、开启FPU,浮点处理单元

__asm void prvEnableVFP( void )
{
  PRESERVE8
  ldr.w r0, =0xE000ED88        ;R0=0XE000ED88  SCB->CPACR寄存器  Coprocessor Access Contrl
  ldr r1, [r0]                 ;从 R0 的地址读取数据赋给 R1
  orr r1, r1, #( 0xf << 20 )   ;R1=R1|(0xf<<20)  bit20~bit23,开启FPU
  str r1, [r0]                 ;R1 中的值写入 R0 保存的地址中
  bx r14
  nop
}

 CPACR Register to enable floating point unit feature; available on Cortex -M4 with floating point unit only

三、开启第一个任务

__asm void prvStartFirstTask( void )
{
    PRESERVE8

    /* Use the NVIC offset register to locate the stack. */
    ldr r0, =0xE000ED08
    ldr r0, [r0]         R0地址处的内容,赋值给R0
    ldr r0, [r0]         获取MSP初始值
    /* Set the msp back to the start of the stack. */
    msr msp, r0          复位MSP
    /* Globally enable interrupts. */
    cpsie i              使能中断,清除PRI MASK
    cpsie f              使能中断,清除FAULT MASK
    dsb                  数据同步屏障?
    isb                  指令同步屏障?
    /* Call SVC to start the first task. */
    svc 0                呼叫SVC异常
    nop
    nop
}

向量表重定位,向量表偏移寄存器(VTOR)。 地址就是 0XE000ED08,通过这个寄存器可以重新定义向量表。

向量表的起始地址保存的就是主栈指针MSP 的初始值。

在 FreeRTOS 中仅仅使用 SVC 异常来启动第一个任务,后面的程序中就再也用不到 SVC 了。

#define   xPortPendSVHandler   PendSV_Handler
__asm void vPortSVCHandler( void ) { PRESERVE8 /* Get the location of the current TCB. */ ldr r3, =pxCurrentTCB 获取pxCurrentTCB的存储地址 ldr r1, [r3] 获取当前任务的TCB存储地址 ldr r0, [r1] TCB第一个字段就是任务堆栈的栈顶指针pxTopOfStack,获取它指向的地址 /* Pop the core registers. */ ldmia r0!, {r4-r11, r14} 多加载/存储指令(R0~R3,R12,PC,xPSR是自动压栈出栈的) msr psp, r0 更新psp任务堆栈指针 isb mov r0, #0 msr basepri, r0 BASEPRI设为0,开中断 bx r14 中断返回,执行PC指向的任务函数. }

1. pxCurrentTCB 是一个指向 TCB_t 的指针,这个指针永远指向正在运行的任务。

2. 前三步的目的就是获取要切换到的这个任务的任务栈顶指针,

因为任务所对应的寄存器值,也就是现场都保存在任务的任务堆栈中,

所以需要获取栈顶指针来恢复这些寄存器值!

3. R14 = LR寄存器,恢复时,它的内容为EXC_RETURN = 0xffff_fffd,是初始化任务堆栈时的值。

========================================================

vTaskStartScheduler 就有 vTaskEndScheduler

/**
 * task. h
 * void vTaskEndScheduler( void );
 *
 * NOTE:  At the time of writing only the x86 real mode port, which runs on a PC
 * in place of DOS, implements this function.
 *
 * Stops the real time kernel tick.  All created tasks will be automatically
 * deleted and multitasking (either preemptive or cooperative) will
 * stop.  Execution then resumes from the point where vTaskStartScheduler ()
 * was called, as if vTaskStartScheduler () had just returned.
 *
 * vTaskEndScheduler () requires an exit function to be defined within the
 * portable layer (see vPortEndScheduler () in port. c for the PC port).  This
 * performs hardware specific operations such as stopping the kernel tick.
 *
 * vTaskEndScheduler () will cause all of the resources allocated by the
 * kernel to be freed - but will not free resources allocated by application
 * tasks.
 *
 */

void vTaskEndScheduler( void )
{ /* Stop the scheduler interrupts and call the portable scheduler end    routine so the original ISRs can be restored if necessary. The port    layer must ensure interrupts enable bit is left in the correct state. */
    portDISABLE_INTERRUPTS();      关中断
    xSchedulerRunning = pdFALSE;
    vPortEndScheduler();           关调度器
}

void vPortEndScheduler( void )
{/* Not implemented in ports where there is nothing to return to.   Artificially force an assert. */
  configASSERT( uxCriticalNesting == 1000UL );  啥都没做~只有x86内核里才使用这个东东。
}

========================================================

========================================================

vTaskSuspendAll() 挂起任务调度器, 调用此函数不需要关闭可屏蔽中断。

void vTaskSuspendAll( void )
{
    /* A critical section is not required as the variable is of type
    BaseType_t.  Please read Richard Barry's reply in the following link to a
    post in the FreeRTOS support forum before reporting this as a bug! -
    http://goo.gl/wu4acr */
    ++uxSchedulerSuspended;
}

uxSchedulerSuspended 挂起嵌套计数器, 调度器挂起是支持嵌套的。

使用函数 xTaskResumeAll()可以恢复任务调度器,调用了几次 vTaskSuspendAll()挂起调度器,同样的也得调用几次 xTaskResumeAll()才会最终恢复任务调度器。

假设现在有这样一种情况, 任务 1 的优先级为 10,此时任务 1 由于等待队列TestQueue 而处于阻塞态 。

但是有段其他的 代码调用函数vTaskSuspendAll()挂起了任务调度器,

在还没有调用 xTaskResumeAll()恢复任务调度器之前,有个在外部中断发生了,在中断服务程序里面调用函数 xQueueSendFromISR()向任务 1 发送了队列 TestQueue

如果任务调度器没有阻塞的话函数 xQueueSendFromISR()会使任务 1 进入就绪态,也就是将任务 1 添加到优先级 10 对应的就绪列表 pxReadyTasksLists[10]中,这样当任务切换的时候任务 1 就会运行。

但是现在任务调度器由于函数 vTaskSuspendAll()而挂起,这个时候任务 1 就不是添加到任务就绪列表 pxReadyTasksLists[10]中了,而是添加到另一个叫做xPendingReadyList 的列表中,

xPendingReadyList 是个全局变量,在文件 tasks.c 中有定义。

当调用函数 xTaskResumeAll()恢复调度器的时候就会将挂到列表 xPendingReadyList 中的任务重新移动到它们所对应的就绪列表 pxReadyTasksLists 中。 

任务调度器恢复

  1 BaseType_t xTaskResumeAll( void )
  2 {
  3 TCB_t *pxTCB = NULL;
  4 BaseType_t xAlreadyYielded = pdFALSE;
  5 
  6     /* If uxSchedulerSuspended is zero then this function does not match a
  7     previous call to vTaskSuspendAll(). */
  8     configASSERT( uxSchedulerSuspended );
  9 
 10     /* It is possible that an ISR caused a task to be removed from an event
 11     list while the scheduler was suspended.  If this was the case then the
 12     removed task will have been added to the xPendingReadyList.  Once the
 13     scheduler has been resumed it is safe to move all the pending ready
 14     tasks from this list into their appropriate ready list. */
 15     taskENTER_CRITICAL();
 16     {
 17         --uxSchedulerSuspended;  嵌套层数变量自减一
 18 
 19         if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )    等于0,要恢复调度器了
 20         {
 21             if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
 22             {
 23                 /* Move any readied tasks from the pending list into the
 24                 appropriate ready list. */  从PendingReadList里面,移动到ReadyList.
 25                 while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
 26                 {
 27                     pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
 28                     ( void ) uxListRemove( &( pxTCB->xEventListItem ) );
 29                     ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
 30                     prvAddTaskToReadyList( pxTCB );
 31 
 32                     /* If the moved task has a priority higher than the current
 33                     task then a yield must be performed. */
 34                     if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )  新出来的优先级高
 35                     {
 36                         xYieldPending = pdTRUE;  切换任务
 37                     }
 38                     else
 39                     {
 40                         mtCOVERAGE_TEST_MARKER();
 41                     }
 42                 }
 43 
 44                 if( pxTCB != NULL )
 45                 {
 46                     /* A task was unblocked while the scheduler was suspended,
 47                     which may have prevented the next unblock time from being
 48                     re-calculated, in which case re-calculate it now.  Mainly
 49                     important for low power tickless implementations, where
 50                     this can prevent an unnecessary exit from low power
 51                     state. */
 52                     prvResetNextTaskUnblockTime();  重新计算变量xNextTaskUnblockTime,详见 “删除任务 ##5”
 53                 }
 54 
 55                 /* If any ticks occurred while the scheduler was suspended then
 56                 they should be processed now.  This ensures the tick count does
 57                 not    slip逃, and that any delayed tasks are resumed at the correct
 58                 time. */
 59                 { 任务调度器挂起时,tick中断不会更新TickCnt,而是更新PendTick【见时间管理章节的TaskIncrementTick函数】
在恢复任务调度时,调用PendTick次TaskIncrementTick()函数,来恢复TickCnt!!
同时,如果有延时任务取消阻塞的动作,也会在TaskIncrementTick函数中进行。 60 UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */ 61 62 if( uxPendedCounts > ( UBaseType_t ) 0U ) 63 { 64 do 65 { 66 if( xTaskIncrementTick() != pdFALSE ) 67 { 68 xYieldPending = pdTRUE; 置位xYieldPending 69 } 70 else 71 { 72 mtCOVERAGE_TEST_MARKER(); 73 } 74 --uxPendedCounts; 75 } while( uxPendedCounts > ( UBaseType_t ) 0U ); 76 77 uxPendedTicks = 0; 78 } 79 else 80 { 81 mtCOVERAGE_TEST_MARKER(); 82 } 83 } 84 85 if( xYieldPending != pdFALSE ) 如果置位了 86 { 87 #if( configUSE_PREEMPTION != 0 ) 88 { 89 xAlreadyYielded = pdTRUE; 90 } 91 #endif 92 taskYIELD_IF_USING_PREEMPTION(); 置位PendSV 93 } 94 else 95 { 96 mtCOVERAGE_TEST_MARKER(); 97 } 98 } 99 } 100 else 101 { 102 mtCOVERAGE_TEST_MARKER(); 103 } 104 } 105 taskEXIT_CRITICAL(); 106 107 return xAlreadyYielded; 108 }

>1 把PendingReadyList里面的任务,恢复到ReadyList当中。(可能引起任务调度)

>2 重新计算NextTaskUnblockTime

>3 弥补在挂起调度器时,漏了的系统滴答时钟节拍。(可能引起任务调度)

========================================================

========================================================

vTaskStepTick()
此函数在使用 FreeRTOS 的低功耗 tickless 模式的时候会用到,即宏 configUSE_TICKLESS_IDLE 为 1。 

当使能低功耗 tickless 模式以后在执行空闲任务的时候系统时钟节拍中断就会停止运行,系统时钟中断停止运行的这段时间必须得补上,
这个工作就是由函数 vTaskStepTick()来完成的。 void vTaskStepTick( const TickType_t xTicksToJump ) {   configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime );   xTickCount += xTicksToJump;   traceINCREASE_TICK_COUNT( xTicksToJump ); } 函数参数 xTicksToJump 是要加上的时间值,系统节拍计数器 xTickCount 加上这个时间值得到新的系统时间。
关于 xTicksToJump 这个时间值的确定。详见“系统低功耗睡眠模式”

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

<think> 嗯,用户的问题是关于FreeRTOS调度器挂起期间恢复任务的作用。首先,我需要仔细理解用户提供的引用内容,尤其是引用[1]到[4]中的关键点。 引用[1]提到任务挂起是将任务从就绪态或运行态移到挂起态,暂停调度和CPU分配,直到显式恢复。引用[2]和[3]则详细说明了调度器挂起函数vTaskSuspendAll()的实现,它通过增加uxSchedulerSuspended计数器来挂起调度器,此时系统节拍中断不会触发任务切换。引用[3]还指出在调度器挂起期间,uxPendedTicks会记录挂起的时间节拍数,以便后续补偿。引用[4]补充了调度器挂起支持嵌套调用,需匹配相同次数的xTaskResumeAll()调用才能恢复。 接下来,用户的核心疑问是:如果在调度器挂起期间恢复某个任务(比如使用vTaskResume()),会发生什么?根据引用[3],当调度器挂起时,即使任务状态改变(如从挂起恢复为就绪态),调度器也不会立即响应,因为uxSchedulerSuspended计数不为零。此时恢复的任务只是被标记为就绪,但不会触发上下文切换。不过,引用[3]中的xYieldPending标志可能在调度器恢复后被检查,从而补发切换请求。 用户可能实际遇到的情景是:在关键代码段(如临界区)挂起调度器后,需要恢复某个任务,但希望理解这种操作的副作用。深层需求可能是确保任务恢复的及时性,同时避免资源冲突。需要强调调度器挂起期间的任务恢复是"延迟生效"的,其执行必须等到调度器恢复后,且补偿节拍会保证时间同步。此外,嵌套调用的注意事项(引用[4])也很关键,避免因计数不匹配导致调度器无法恢复。 最后,生成的相关问题需覆盖调度器挂起机制、任务状态延迟更新的影响、嵌套调用的最佳实践,以及如何调试此类场景。确保回答既解答作用机制,又提供实用建议。 </think> 在FreeRTOS中,**调度器挂起期间恢复任务不会立即生效,但会记录任务状态变更请求,待调度器恢复后统一处理**。具体作用机制如下: ### 核心机制分析 1. **延迟响应恢复请求** 调用 `vTaskResume()` 恢复任务时: - 若调度器挂起(`uxSchedulerSuspended > 0`),任务会被标记为 **就绪态**,但**不会立即加入调度队列**[^1][^3] - 实际状态切换需等待调度器恢复(`xTaskResumeAll()`) 2. **时间片补偿机制** 调度器挂起期间: - 系统节拍中断仍会触发,但仅累加挂起节拍数 `uxPendedTicks++`[^3] - 任务延迟计算暂停,确保恢复后时间同步 3. **恢复时的批量处理** 调用 `xTaskResumeAll()` 时: ```c void xTaskResumeAll(void) { // 步骤1:减少挂起计数 --uxSchedulerSuspended; // 步骤2:若嵌套计数归零 if(uxSchedulerSuspended == 0) { // 补偿挂起期间遗漏的节拍 while(uxPendedTicks > 0) { xTaskIncrementTick(); // 处理任务延迟和超时 --uxPendedTicks; } // 步骤3:执行挂起期间累积的任务状态变更 if(xYieldPending == pdTRUE) { taskYIELD(); // 触发任务切换 } } } ``` - 补执行所有遗漏的系统节拍中断[^3] - 处理期间累积的就绪任务加入调度队列 - 触发任务切换(若有待切换请求) ### 应用场景与作用 1. **关键资源保护** 在操作共享资源(如全局链表)时挂起调度器,期间恢复任务可避免实时性任务错过就绪时机,同时保证资源操作的原子性[^2]。 2. **批量任务状态管理** 例如中断服务程序中需恢复多个任务时: ```c void ISR_Handler() { vTaskSuspendAll(); // 挂起调度器 xTaskResume(taskA); // 延迟生效 xTaskResume(taskB); // 延迟生效 xTaskResumeAll(); // 统一处理恢复请求 } ``` 减少多次切换的开销,提升效率[^4]。 3. **时间敏感型操作** 调度器挂起期间恢复高优先级任务,确保在 `xTaskResumeAll()` 后该任务能**立即抢占CPU**,满足实时性要求[^1]。 > ⚠️ **注意事项** > - 调度器挂起期间不允许调用可能引发阻塞的API(如 `vTaskDelay()`) > - 嵌套调用时需严格匹配 `vTaskSuspendAll()` 和 `xTaskResumeAll()` 次数[^4] > - 最长挂起时间需小于看门狗超时阈值 ### 总结作用 调度器挂起期间恢复任务的本质是 **"延迟执行状态变更"**,其核心价值在于: 1. 保证关键代码段的原子性 2. 减少高频状态切换的开销 3. 通过节拍补偿机制维护系统时间一致性 4. 为实时任务提供精确的唤醒控制 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值