时间片调度

← 链表和调度 | ← FreeRTOS内核实现与应用开发实战指南知识地图


调度的几种方式(回顾)

调度方式开关说明
优先级抢占调度configUSE_PREEMPTION=1(默认)高优先级任务就绪立即抢占,正在运行的低优先级任务被踢出
时间片轮转调度configUSE_TIME_SLICING=1(默认)同优先级任务轮流执行,每个任务 1 个 tick 时间片(本页主题)
协作式调度configUSE_PREEMPTION=0不抢占,只有任务主动让出(阻塞 /taskYIELD())才切换
事件唤醒调度信号量/队列 APIGive/Send 唤醒等待任务并抢占,见链表和调度 信号任务的调度
延时到期调度SysTick阻塞/延时到期任务回就绪链表,见链表和调度 延时任务的调度、空闲任务与阻塞延时

时间片轮转:同优先级轮流跑

每个任务默认分到一个 tick 的时间片(tick 周期由 configTICK_RATE_HZ 决定,如 1000Hz → 1ms)。时间片用完(下一次 SysTick 到来)时,若同优先级就绪链表上还有别的任务,就把 CPU 让给下一个,依次轮转:

任务A(优先级3) → 跑1个tick → 任务B(优先级3) → 跑1个tick → 任务A → ...

执行流程(SysTick → PendSV):

  1. SysTick 中断 → xTaskIncrementTick():xTickCount 加 1,检查当前任务时间片是否用完
  2. 检查当前优先级链表长度:> 1 说明还有同优先级任务排队 → 标记需要切换,悬起 PendSV
  3. vTaskSwitchContext() 选任务:taskSELECT_HIGHEST_PRIORITY_TASK() 挑最高优先级链表,listGET_OWNER_OF_NEXT_ENTRY() 把链表指针挪到下一个节点——这就是轮转的实现
  4. 上下文切换细节见 链表和调度:压栈 CPU 通用寄存器组和特殊寄存器(硬件自动 + 软件手动)、更换 pxCurrentTCB 栈顶指针、恢复新任务现场更新 PSP,无感切换

细节:

  • 只有自己一个:当前优先级链表长度 = 1,时间片到期也不切换,省掉无谓的上下文切换开销
  • 提前阻塞:任务在时间片内主动阻塞(延时/等信号量),它已从就绪链表摘除,自然谈不上轮转
  • 高优先级插队:时间片到期时若有更高优先级任务就绪,调度器仍优先切换给高优先级,轮转只发生在同优先级之间