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 完成,确保中断处理的实时性。

相关推荐
周杰偷奶茶1 小时前
【C 语言】分支、循环和 goto 语句(附实战)
c语言
LuminousCPP1 小时前
数据结构-排序(五):计数排序与排序专题阶段总结|从外部归并到线性排序,再看算法边界与选型
c语言·数据结构·笔记·算法·排序算法
山下梅子酒2251 小时前
洛谷-入门-B2046
c语言
李永奉2 小时前
中科蓝讯SDK开发-耳机项目功耗问题排查教程
c语言·开发语言·嵌入式硬件·物联网·智能手机
linx2952 小时前
第七章 · 标准库容器、算法与 ranges
c语言·开发语言·数据结构·c++·算法
Logic1012 小时前
C语言/数据结构位运算题解:异或XOR找出独特数字的索引位置——成对数字在两侧
c语言·数据结构·数组·位运算·时间复杂度·算法题·异或性质
xiangyun6115 小时前
【408数据结构 08】队列:循环队列判空判满,408年年考,一次讲清
c语言·开发语言·数据结构·c++·算法
xiangyun6116 小时前
【408数据结构 06】双链表、循环链表、静态链表
c语言·开发语言·数据结构·c++·算法
Logic10117 小时前
C语言/数据结构数组题解:数字列表加一(Plus One)——进位处理与数组扩展
c语言·数据结构·模拟·数组·时间复杂度·算法题·进位