configUSE_TIME_SLICING和configUSE_PREEMPTION

preemption: 抢占式

slicing:切片

configUSE_PREEMPTION:抢占式有衔接

  • 配置为 1(抢占式调度) :这是 FreeRTOS 的默认模式 ,也是绝大多数实时应用的选择。调度器始终运行当前优先级最高且处于就绪态的任务 。一旦有更高优先级的任务就绪(例如被中断唤醒),它会立即抢占 当前正在运行的低优先级任务,无需等待当前任务主动让出 CPU。这保证了系统的实时响应能力。

  • 配置为 0(合作式调度) :也称为协同调度。在此模式下,高优先级任务无法抢占低优先级任务

复制代码
configUSE_TIME_SLICING :同等优先级的任务,通过时间切换切换
  • 配置为 1(启用) :当多个任务具有相同优先级且都处于就绪态时,调度器会在每个 Tick 中断(即一个时间片,长度等于 1 / configTICK_RATE_HZ)将当前运行任务移到同优先级就绪链表的末尾,然后选择链表头部的任务运行。这样,同优先级任务会轮流执行,每个任务每次运行一个 Tick。

  • 配置为 0(关闭) :同优先级任务之间不会因为时间片用完而自动切换。一个任务会一直运行,直到它:

⚙️ 与 configUSE_PREEMPTION 的协同关系

configUSE_TIME_SLICING 的效果高度依赖于 configUSE_PREEMPTION。下表总结了不同组合下的调度行为:

configUSE_PREEMPTION configUSE_TIME_SLICING 调度行为
1 (抢占式) 1 (启用) 默认模式。高优先级任务可抢占低优先级;同优先级任务按时间片轮转。
1 (抢占式) 0 (关闭) 高优先级任务可抢占;但同优先级任务不会自动轮转。一个任务会持续运行,直到它主动阻塞、让出或被更高优先级抢占。
0 (合作式) 任意 高优先级任务不能 抢占。任务切换只能由任务主动触发(阻塞、taskYIELD() 等)。此时 configUSE_TIME_SLICING 实际上无效,因为调度器不会在 Tick 中断中强制切换。

下面逐行解读:

c

复制代码
#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
  • 条件编译 :只有同时启用了抢占式调度 (configUSE_PREEMPTION == 1)和时间片轮转 (configUSE_TIME_SLICING == 1)时,这段代码才会被编译进内核。

  • 如果任一宏为 0,这段逻辑不存在,同优先级任务不会因时间片耗尽而自动切换。

c

复制代码
        {
            if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
  • pxCurrentTCB:指向当前正在运行任务的 TCB(任务控制块)。

  • pxCurrentTCB->uxPriority:当前任务的优先级。

  • pxReadyTasksLists[ ... ]:对应优先级的就绪任务列表。

  • listCURRENT_LIST_LENGTH:宏,用于获取该列表的当前节点数量(即该优先级下处于就绪态的任务个数)。

  • 判断条件:当前优先级的就绪任务数量是否大于 1。

    • 如果大于 1,说明有多个同优先级任务就绪,需要轮转。

    • 如果等于 1,说明只有当前任务自己就绪,无需切换。

c

复制代码
            {
                xSwitchRequired = pdTRUE;
            }
  • xSwitchRequired 是一个局部变量,用于标记本次节拍中断是否需要触发任务切换。

  • 设置为 pdTRUE 后,在 xTaskIncrementTick() 返回时,系统会根据这个标志决定是否触发 PendSV 异常,从而在中断退出时进行上下文切换。

c

复制代码
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
  • mtCOVERAGE_TEST_MARKER() 是用于代码覆盖率测试的宏,在非测试模式下通常为空语句,不影响运行逻辑。

🎯 整体作用

在每次系统节拍(Tick)中断中,FreeRTOS 会执行 xTaskIncrementTick()。这段代码检查当前任务所在优先级是否还有其它就绪任务:

  • 有 → 设置 xSwitchRequired = pdTRUE,请求切换。

  • 没有 → 不请求切换。

最终,xTaskIncrementTick() 返回 xSwitchRequired。在 xPortSysTickHandler 中,如果返回 pdTRUE,就会挂起 PendSV 异常,在中断退出后执行任务切换。切换时,调度器会把当前任务移到同优先级就绪链表的末尾,然后从链表头部取出下一个任务运行,从而实现同优先级任务的时间片轮转。

🔗 与配置宏的关系

宏 作用
configUSE_PREEMPTION 总开关,决定是否允许高优先级任务抢占。为 0 时,整个时间片轮转逻辑不编译。
configUSE_TIME_SLICING 控制同优先级任务是否在 Tick 中断中自动轮转。为 0 时,即使抢占开启,同优先级任务也不会因时间片耗尽而切换。

因此,这段代码只有在 抢占式 + 时间片轮转 同时开启时才生效,是 FreeRTOS 默认配置下实现公平调度的核心机制。

⚠️ 注意

  • 如果 configUSE_TIME_SLICING = 0,同优先级任务不会自动轮转,一个任务会持续运行,直到它主动阻塞、调用 taskYIELD() 或被更高优先级任务抢占。

  • xSwitchRequired 只是一个"请求",实际切换发生在中断退出时,由 PendSV 完成,确保中断处理的实时性。

相关推荐
yyy(十一月限定版)8 小时前
224redfg
c语言
小师兄吃牛肉10 小时前
C语言中的nullptr有必要吗
c语言·c++·算法
lhbweilai12 小时前
深挖FreeRTOS内核实现(三)
c语言·stm32·单片机
xxwl58512 小时前
数据结构知识点和代码实现总结(C语言实现)
c语言·开发语言·数据结构
小灰灰搞电子13 小时前
C语言 <time.h> 头文件详解
c语言·time.h
C++ 老炮儿的技术栈15 小时前
sizeof操作符
c语言·c++·人工智能·mfc·c
wuminyu15 小时前
ForkJoinPool内部WorkQueue的Lock-Free数组操作以及并发任务窃取原理剖析
java·linux·c语言·jvm·c++
她说彩礼65万15 小时前
C语言 堆区和栈区
java·linux·c语言
小师兄吃牛肉17 小时前
C语言基本语法汇总
c语言
无忧.芙桃17 小时前
C++ 并查集详解:原理、实现与应用
c语言·c++·算法·leetcode