学习RTOS(9)支持时间片

FreeRTOS 与隔壁的 RT-Thread 和 μC/OS 一样,都支持时间片的功能。 所谓时间片就是同一个优先级下可以有多个任务,每个任务轮流地享有相同的 CPU 时间, 享有 CPU 的时间我们叫时间片。在 RTOS 中,最小的时间单位为一个 tick,即 SysTick 的中断周期,RT-Thread 和 μC/OS 可以指定时间片的大小为多个 tick,但是 FreeRTOS 不一样,时间片只能是一个 tick。 与其说 FreeRTOS 支持时间片,倒不如说它的时间片就是正常的任务调度。

假设目前系统中有三个任务就绪(算上空闲任务就是 4 个),任务 1 和任务 2 的优先级为 2,任务 3 的优先级为 3, 整个就绪列表的示意图
在这里插入图片描述

为了方便在逻辑分析仪中地分辨出任务 1 和任务 2 使用的时间片大小,任务 1 和任务2 的主体编写成一个无限循环函数,不会阻塞, 任务 3 的阻塞时间设置为 1 个 tick。任务 1和任务 2 的任务主体编写为一个无限循环, 这就意味着,优先级低于 2 的任务就会被饿死,得不到执行,比如空闲任务。

先定义第三个任务在这里插入图片描述

创建第三个任务,并且将任务1和任务2 的优先级都设置为同级别2

在这里插入图片描述

为了方便观察任务 1 和任务 2 使用的时间片大小,特意将任务的主体编写成一个无限循环。实际项目中不会这样使用,否则低于任务 1 和任务 2 优先级的任务就会被饿死,一直没有机会被执行。

因为任务 1 和任务 2 的主体是无限循环的,要想任务 3 有机会执行,其优先级就必须高于任务 1 和任务 2 的优先级。 为了方便观察任务 1 和任务 2 使用的时间片大小,任务 3 的阻塞延时设置为 1 个 tick。
在这里插入图片描述

实验现象
进入软件调试,全速运行程序,从逻辑分析仪中可以看到任务 1 和任务 2 轮流执行,每一次运行的时间等于任务 3 中 flag3 输出高电平或者低电平的时间,即一个 tick。
在这里插入图片描述

任务1 和 任务2 是非阻塞工作,当获取到CPU时间的时候会一直执行不会退出,知道1个tick时间到了,被强制释放了CPU的使用权,才会切换到另个任务执行。而任务3是阻塞延时,会立刻释放CPU的使用权给其他任务使用。
在这里插入图片描述

原理分析
任务1 和 任务2 同属于一个优先级别,那么程序是如何保证每个任务都能执行到。

taskSELECT_HIGHEST_PRIORITY_TASK()函数
在这里插入图片描述

寻找就绪任务的最高优先级。即根据优先级位图表uxTopReadyPriority 找到就绪任务的最高优先级,然后将优先级暂存在 uxTopPriority。获取优先级最高的就绪任务的 TCB,然后更新到 pxCurrentTCB。

listGET_OWNER_OF_NEXT_ENTRY()函数
在这里插入图片描述

listGET_OWNER_OF_NEXT_ENTRY()函数的妙处在于它并不是获取链表下的第一个节点的 OWNER,而且用于获取下一个节点的 OWNER。有下一个那么就会有上一个的说法,怎么理解?假设当前链表有 N 个节点,当第 N 次调用该函数时, pxIndex 则指向第 N个节点, 即每调用一次, 节点遍历指针 pxIndex 则会向后移动一次,用于指向下一个节点。

本实验中,优先级 2 下有两个任务,当系统第一次切换到优先级为 2 的任务(包含了任务 1 和任务 2,因为它们的优先级相同) 时, pxIndex 指向任务 1, 任务 1 得到执行。 当任务 1 执行完毕,系统重新切换到优先级为 2 的任务时, 这个时候 pxIndex 指向任务 2,任务 2 得到执行, 任务 1 和任务 2 轮流执行,享有相同的 CPU 时间, 即所谓的时间片。

本实验中,任务 1 和任务 2 的主体都是无限循环,那如果任务 1 和任务 2 都会调用将自己挂起的函数(实际运用中,任务体都不能是无限循环的,必须调用能将自己挂起的函数) ,比如 vTaskDelay()。 调用能将任务挂起的函数中,都会先将任务从就绪列表删除,然后将任务在优先级位图表uxTopReadyPriority 中 对 应 的 位 清 零 , 这 一 功 能 由taskRESET_READY_PRIORITY()函数来实现, 该函数在 task.c 中定义,具体实现见代码清

taskRESET_READY_PRIORITY()函数
在这里插入图片描述

taskRESET_READY_PRIORITY() 函 数 的 妙 处 在 于 清 除 优 先 级 位 图 表uxTopReadyPriority 中相应的位时候,会先判断当前优先级链表下是否还有其它任务,如果有则不清零。 假设当前实验中,任务 1 会调用 vTaskDelay(),会将自己挂起,只能是将任务 1 从就绪列表删除,不能将任务 1 在优先级位图表 uxTopReadyPriority 中对应的位清 0,因为该优先级下还有任务 2,否则任务 2 将得不到执行。
在这里插入图片描述

当xTaskIncrementTick()函数返回为真时才进行任务切换, 原来的 xTaskIncrementTick()是不带返回值的, 执行到最后会调用 taskYIELD()执行任务切换。
在这里插入图片描述
在这里插入图片描述

代码清单 12-6(1): 将 xTaskIncrementTick()函数修改成带返回值的函数。
代 码 清 单 12-6(2) :定义一个局部变量 xSwitchRequired ,用于存储xTaskIncrementTick()函数的返回值,当返回值是 pdTRUE 时,需要执行一次任务切换,默认初始化为 pdFALSE。
代码清单 12-6(3): configUSE_PREEMPTION 是在 FreeRTOSConfig.h 的一个宏,默认为 1,表示有任务就绪且就绪任务的优先级比当前优先级高时,需要执行一次任务切换,即将 xSwitchRequired 的值置为 pdTRUE。 在 xTaskIncrementTick()函数还没有修改成带返回值的时候,我们是在执行完 xTaskIncrementTick()函数的时候,不管是否有任务就绪,不管就绪的任务的优先级是否比当前任务优先级高都执行一次任务切换。如果就绪任务的优先级比当前优先级高,那么执行一次任务切换与加了代码清单 12-6(3)这段代码实现的功能是一样的。如果没有任务就绪呢?就不需要执行任务切换,这样与之前的实现方法相比就省了一次任务切换的时间。虽然说没有更高优先级的任务就绪,执行任务切换的时候还是会运行原来的任务,但这是以多花一次任务切换的时间为代价的。

代码清单 12-6(4): 这部分与时间片功能相关。 当 configUSE_PREEMPTION 与configUSE_TIME_SLICING 都为真, 且当前优先级下不止一个任务时就执行一次任务切换,即将 xSwitchRequired 置为 pdTRUE 即可。 在 xTaskIncrementTick()函数还没有修改成带返回 值 之 前 , 这 部 分 代 码 不 需 要 也 是 可 以 实 现 时 间 片 功 能 的 , 即 只 要 在 执 完xTaskIncrementTick() 函 数 后 执 行 一 次 任 务 切 换 即 可 。 configUSE_PREEMPTION 在FreeRTOSConfig.h 中默认定义为 1, configUSE_TIME_SLICING 如果没有定义, 则会默认在 FreeRTOS.h 中定义为 1。
其实 FreeRTOS 的这种时间片功能不能说是真正意义的时间片,因为它不能随意的设置时间为多少个 tick,而是默认一个 tick,然后默认在每个 tick 中断周期中进行任务切换而已。

代码清单 12-6(5): 不在这里进行任务切换,而是放到了 xPortSysTickHandler()函数中。当 xTaskIncrementTick()函数的返回值为真时才进行任务切换。

Hankin
2020.08.22

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值