当前位置: 首页 > news >正文

FreeRTOS学习笔记(8):时间片轮转机制

时间片轮转机制总结

1. 时间片的概念与应用

时间片(Time Slice) 是操作系统为每个任务分配的一段连续 CPU 运行时间。在时间片轮转调度中,所有就绪的同优先级任务依次获得一个时间片,时间片用完后调度器强制切换到下一个任务,从而保证每个任务都能公平地占用 CPU,避免某个任务长时间独占。

在 FreeRTOS 中,时间片的大小等于一个系统时钟节拍(Tick),节拍频率由 configTICK_RATE_HZ 配置(例如 1000 Hz 表示 1 ms 一个时间片)。当同时有多个相同优先级的任务处于就绪状态时,调度器会在每个节拍中断到来时,检查当前任务所在的优先级队列中是否还有其它就绪任务,如果有,则触发任务切换,将 CPU 交给队列中的下一个任务。这样,这些同优先级的任务就会轮流运行,每个任务每次最多运行一个时间片。


2. 时间片测试实验

实验目的:验证相同优先级的任务能够按时间片轮转执行,而高优先级任务可抢占低优先级任务。

实验任务

  • Task1(优先级 2):循环设置 flag1 = 1 → 延时 100 个空循环 → flag1 = 0 → 延时 100。
  • Task2(优先级 2):与 Task1 相同,操作 flag2
  • Task3(优先级 3):循环设置 flag3 = 1vTaskDelay(1)flag3 = 0vTaskDelay(1)

关键点

  • Task1 与 Task2 优先级相同(2),Task3 优先级更高(3)。
  • 通过逻辑分析仪观察 flag1flag2flag3 的波形,分析任务调度行为。

2.1 main 函数分析

main.c 中的 main() 函数主要完成以下工作:

  1. 静态创建三个任务
    • 调用 xTaskCreateStatic(),分别指定任务函数、名称、栈大小、优先级、栈缓冲区和 TCB。
    • Task1 和 Task2 优先级均为 2,Task3 优先级为 3。
  2. 禁止中断portDISABLE_INTERRUPTS())。
  3. 启动调度器vTaskStartScheduler()),该函数会创建空闲任务并启动系统时钟,随后不再返回。

vApplicationGetIdleTaskMemory() 提供了空闲任务的栈和 TCB 内存。

main.c



3. 实验现象

image

  • 高优先级抢占:Task3(优先级 3)一旦就绪(例如延时结束),会立即抢占 Task1/Task2 执行,因此 flag3 波形呈现周期性高电平,间隔为 vTaskDelay(1) 的时间。
  • 时间片轮转:当 Task3 阻塞时,系统在优先级 2 的就绪列表中选择下一个任务。由于 Task1 和 Task2 处于同一优先级,它们会轮流获得 CPU,每个任务运行一个系统节拍(时间片)后被切换。波形上 flag1flag2 交替出现高电平,且每个高电平持续时间大致相等(约一个时间片),这正是时间片轮转的直接体现。

4. 原理分析

4.1 taskSELECT_HIGHEST_PRIORITY_TASK() 函数

该函数用于在任务切换时找出当前最高优先级的就绪任务,并更新 pxCurrentTCB

在通用实现中(未使用处理器优化),其逻辑为:

#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \UBaseType_t uxTopPriority = uxTopReadyPriority; \/* 从最高优先级向下查找第一个非空就绪列表 */ \while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \{ \--uxTopPriority; \} \/* 取出该列表中的第一个任务(轮转时使用 listGET_OWNER_OF_NEXT_ENTRY 实现轮换)*/ \listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \/* 更新最高优先级记录 */ \uxTopReadyPriority = uxTopPriority; \
}

关键点

  • uxTopReadyPriority 始终记录当前就绪任务的最高优先级,避免每次从最高可能优先级开始扫描。
  • 如果某个优先级有多个任务,listGET_OWNER_OF_NEXT_ENTRY 会依次返回列表中的下一个任务,从而实现同优先级任务的轮转(时间片轮转的基础)。

4.2 taskRESET_READY_PRIORITY() 函数

当某个优先级的所有任务都不再就绪(例如全部阻塞或删除)时,需要将该优先级在就绪优先级位图中清除,以便 taskSELECT_HIGHEST_PRIORITY_TASK() 能正确找到新的最高优先级。

在优化版本中,该宏定义为:

#define taskRESET_READY_PRIORITY( uxPriority ) \
{ \if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \{ \portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \} \
}

作用

  • 检查对应优先级的就绪列表是否为空,若为空则调用 portRESET_READY_PRIORITY() 清除该优先级的标志位。
  • 在通用实现中,该宏定义为空,因此最高优先级扫描时仍会遍历列表,性能略低。

5. 修改代码,支持时间片轮转

FreeRTOS 的时间片轮转功能依赖于两个配置宏:

  • configUSE_PREEMPTION:启用抢占式调度(必须为 1)。
  • configUSE_TIME_SLICING:启用时间片轮转(默认为 1)。

task.cxTaskIncrementTick() 函数中,除了处理延时任务的唤醒外,还包含了时间片轮转的代码段:

#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
{/* 如果当前任务所在优先级的就绪列表中还有其它任务,则请求切换 */if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 ){xSwitchRequired = pdTRUE;}
}
#endif

作用

  • 每次系统时钟节拍中断时,检查当前任务所在优先级是否有多个就绪任务。
  • 如果有,则标记 xSwitchRequired = pdTRUE,在中断退出时触发任务切换,从而实现同优先级任务的时间片轮转。

5.1 xPortSysTickHandler() 函数与 xTaskIncrementTick() 的修改

xPortSysTickHandler() 是处理器移植层的中断处理函数,通常会在其中调用 xTaskIncrementTick()。若需要支持时间片轮转,只需确保:

  • 配置文件中 configUSE_TIME_SLICING 定义为 1。
  • 中断处理函数中正确调用 xTaskIncrementTick(),并检查其返回值 xSwitchRequired,若为 pdTRUE 则触发 PendSV 进行任务切换。

在提供的 task.c 中,xTaskIncrementTick() 已经包含了时间片轮转代码,因此无需额外修改,只需保证相关宏已正确定义。


总结

  • 时间片轮转 通过 taskSELECT_HIGHEST_PRIORITY_TASK() 在同一优先级列表中循环取出任务,以及 xTaskIncrementTick() 在每个时钟节拍判断是否需要切换来实现。
  • 实验现象验证了高优先级任务抢占和同优先级任务交替执行的调度行为。
  • 理解这些代码有助于深入掌握 FreeRTOS 调度器的核心机制。
http://www.jsqmd.com/news/540595/

相关文章:

  • 【shell编程】深入解析bash: bad file descriptor:从原理到实战避坑指南
  • 免费获取Cherry MX键帽3D模型:打造个性化机械键盘的终极指南
  • AMS1117-1.2v可以替代AMS1117-ADJ吗?
  • 3步构建企业级流程:wflow无代码设计器实战指南
  • rust项目rustc版本不够报错
  • Qwen3-ASR-1.7B部署教程:GPU温度监控与过热降频应对策略
  • 2026国内旋光仪供应商推荐:行业合作优选指南 - 品牌排行榜
  • 深度学习道路提取代码更换数据集后 PyCharm 闪退问题全面解决指南
  • 开源CTF解题利器:从线性操作到可视化工作流的革命性进化
  • Cursor Pro功能激活与限制突破技术实现指南
  • Qwen3-Reranker-8B基础教程:vLLM量化部署(AWQ/GGUF)实测对比
  • phpmailer和swiftmailer发信SMTP
  • Z-Image-ComfyUI新手入门:无需代码,一键生成高质量AI图像
  • 如何快速掌握FLAC:面向音乐爱好者的完整无损音频压缩指南
  • 游戏开发中的流水线优化:从CPU冒险问题到GPU并行计算
  • 图片防御与lvlm攻击论文阅读笔记
  • OpenClaw配置加密:GLM-4.7-Flash连接凭证的安全存储方案
  • League-Toolkit:英雄联盟辅助工具的效率提升与战术优化指南
  • SDMatte与前端Vue.js结合:打造交互式在线抠图工具
  • GetQzonehistory:数字记忆守护的终极方案
  • FinFET技术如何重塑现代芯片设计?
  • 别再只盯着GDP了!用Python+GIS手把手教你计算城市土地利用强度指数(附代码与数据)
  • 3D打印机步进电机参数计算全攻略:从同步带到丝杆的实战配置
  • 避坑指南:用FragmentStateAdapter优化ViewPager卡片内存泄漏问题
  • 立创K230庐山派Linux小核实战:从零配置WiFi模块与网络调试
  • Shardingsphere-Proxy 5.5.0部署避坑指南:从配置文件到数据库连接的全流程解析
  • 如何快速下载网易云音乐双语歌词:LrcHelper完整指南
  • 高效PDF处理:用PDF Arranger实现极简文档管理
  • 【PyCharm】解决gensim安装难题:从环境配置到镜像源优化
  • 3步解锁苹果电脑新玩法:用PlayCover畅玩iOS游戏和应用