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