学习RTOS(7)支持多优先级

如何支持多优先级
就绪列表 pxReadyTasksLists[ configMAX_PRIORITIES ]是一个数组, 数组里面存的是就绪任务的 TCB(准确来说是 TCB 里面的 xStateListItem 节点) ,数组的下标对应任务的优先级,优先级越低对应的数组下标越小。空闲任务的优先级最低,对应的是下标为 0 的链表。 图 10-1 演示的是就绪列表中有两个任务就绪, 优先级分别为 1 和 2,其中空闲任务没有画出来,空闲任务自系统启动后会一直就绪,因为系统至少得保证有一个任务可以运行。
在这里插入图片描述

pxCurrenTCB 是一个全局的 TCB 指针,用于指向优先级最高的就绪任务的 TCB,即当前正在运行的 TCB。那么我们要想让任务支持优先级,即只要解决在任务切换( taskYIELD) 的时候,让 pxCurrenTCB 指向最高优先级的就绪任务的 TCB 就可以,前面的章节我们是手动地让 pxCurrenTCB 在任务 1、任务 2 和空闲任务中轮转,现在我们要改成 pxCurrenTCB 在任务切换的时候指向最高优先级的就绪任务的 TCB 即可,那问题的关键就是:如果找到最高优先级的就绪任务的 TCB。 FreeRTOS 提供了两套方法,一套是通用的。在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代 码 清 单 10-1(1) :查找最高优先级的就绪任务有两种方法,具体由configUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏控制, 定义为 0 选择通用方法,定义为 1 选择根据处理器优化的方法,该宏默认在 portmacro.h 中定义为 1,即使用优化过的方法。

通用方法

代码清单 10-1(2): taskRECORD_READY_PRIORITY()用于更新 uxTopReadyPriority的值。 uxTopReadyPriority 是一个在 task.c 中定义的静态变量, 用于表示创建的任务的最高优先级, 默认初始化为 0,即空闲任务的优先级。
在这里插入图片描述

代码清单 10-1(3): taskSELECT_HIGHEST_PRIORITY_TASK()用于寻找优先级最高的就绪任务, 实质就是更新 uxTopReadyPriority 和 pxCurrentTCB 的值。
代码清单 10-1(3)-①: 将 uxTopReadyPriority 的值暂存到局部变量 uxTopPriority,接下来需要用到
代码清单 10-1(3)-②:从最高优先级对应的就绪列表数组下标开始寻找当前链表下是否有任务存在,如果没有,则 uxTopPriority 减一操作,继续寻找下一个优先级对应的链表中是否有任务存在, 如果有则跳出 while 循环,表示找到了最高优先级的就绪任务。 之所以可以采用从最高优先级往下搜索,是因为任务的优先级与就绪列表的下标是一一对应的,优先级越高,对应的就绪列表数组的下标越大。
代码清单 10-1(3)-③: 获取优先级最高的就绪任务的 TCB,然后更新到 pxCurrentTCB。
代码清单 10-1(3)-④: 更新 uxTopPriority 的值到 uxTopReadyPriority。

优化方法
代码清单 10-1(4):优化的方法,这得益于 Cortex-M 内核有一个计算前导零的指令CLZ,所谓前导零就是计算一个变量( Cortex-M 内核单片机的变量为 32 位)从高位开始第一次出现 1 的位的前面的零的个数。 比如: 一个 32 位的变量 uxTopReadyPriority, 其位 0、位 24 和 位 25 均 置 1 , 其 余 位 为 0 , 具 体 见 。 那 么 使 用 前 导 零 指 令 __CLZ(uxTopReadyPriority)可以很快的计算出 uxTopReadyPriority 的前导零的个数为 6。在这里插入图片描述

如果 uxTopReadyPriority 的每个位号对应的是任务的优先级,任务就绪时,则将对应的位置 1,反之则清零。那么图 10-2 就表示优先级 0、优先级 24 和优先级 25 这三个任务就绪,其中优先级为 25 的任务优先级最高。利用前导零计算指令可以很快计算出就绪任务中的最高优先级为: ( 31UL - ( uint32_t ) __clz( ( uxReadyPriorities ) ) ) = ( 31UL - uint32_t )6 ) = 25。
代码清单 10-1(5): taskRECORD_READY_PRIORITY()用于根据传进来的形参(通常形参就是任务的优先级) 将变量 uxTopReadyPriority 的某个位置 1。 uxTopReadyPriority 是一个在 task.c 中定义的静态变量,默认初始化为 0。与通用方法中用来表示创建的任务的最高优先级不一样,它在优化方法中担任的是一个优先级位图表的角色,即该变量的每个位对应任务的优先级,如果任务就绪,则将对应的位置 1,反之清零。根据这个原理,只需要计算出 uxTopReadyPriority 的前导零个数就算找到了就绪任务的最高优先级。 与taskRECORD_READY_PRIORITY() 作 用 相 反 的 是 taskRESET_READY_PRIORITY() 。taskRECORD_READY_PRIORITY()与 taskRESET_READY_PRIORITY()具体的实现见代码在这里插入图片描述

代码清单 10-1(6): taskRESET_READY_PRIORITY()用于根据传进来的形参(通常形参就是任务的优先级) 将变量 uxTopReadyPriority 的某个位清零。
代码清单 10-1(注意):实际上根据优先级调用 taskRESET_READY_PRIORITY()函数复位 uxTopReadyPriority 变量中对应的位时, 要先确保就绪列表中对应该优先级下的链表没有任务才行。 但是我们当前实现的阻塞延时方案还是通过扫描就绪列表里面的 TCB 的延时变量 xTicksToDelay 来实现的, 还没有单独实现延时列表(任务延时列表将在下一个章节讲解),所以任务非就绪时暂时不能将任务从就绪列表移除,而是仅仅通过将任务优先级在变量 uxTopReadyPriority 中对应的位清零。 在下一章我们实现任务延时列表之后, 任务非就绪时, 不仅会将任务优先级在变量 uxTopReadyPriority 中对应的位清零,还会将任务从就绪列表删除。
代码清单 10-1(7): taskSELECT_HIGHEST_PRIORITY_TASK()用于寻找优先级最高的就绪任务, 实质就是更新 uxTopReadyPriority 和 pxCurrentTCB 的值。
代码清单 10-1(7)-①: 根据 uxTopReadyPriority 的值, 找到最高优先级, 然后更新到xTopPriority 这个局部变量中。 portGET_HIGHEST_PRIORITY()具体的宏实现见代码清单10-4,在 portmacro.h 中定义。在这里插入图片描述

代码清单 10-1(7)-②: 根据 uxTopPriority 的值, 从就绪列表中找到就绪的最高优先级的任务的 TCB,然后将 TCB 更新到 pxCurrentTCB。

增加优先级形参, 数值越大,优先级越高。
在这里插入图片描述

初始化任务优先级
在这里插入图片描述

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

代 码 清 单 10-8(1) :全局任务计时器 uxCurrentNumberOfTasks 加一操作。uxCurrentNumberOfTasks 是一个在 task.c 中定义的静态变量,默认初始化为 0
代码清单 10-8(2): 如果 pxCurrentTCB 为空,则将 pxCurrentTCB 指向新创建的任务。pxCurrentTCB 是一个在 task.c 定义的全局指针,用于指向当前正在运行或者即将要运行的任务的任务控制块,默认初始化为 NULL。
代码清单 10-8(3): 如果是第一次创建任务,则需要调用函数 prvInitialiseTaskLists()初始化任务相关的列表, 目前只有就绪列表需要初始化, 该函数在 task.c 中定义,具体实现在这里插入图片描述

代码清单 10-8(4): 如果 pxCurrentTCB 不为空,表示当前已经有任务存在,则根据任务的优先级将 pxCurrentTCB 指向最高优先级任务的 TCB。在创建任务时,始终让pxCurrentTCB 指向最高优先级任务的 TCB。
代码清单 10-8(5): 将任务添加到就绪列表。 prvAddTaskToReadyList()是一个带参宏,在 task.c 中定义,具体实现见代码清单 10-10。在这里插入图片描述

代码清单 10-10(1):根据优先级将优先级位图表 uxTopReadyPriority 中对应的位置位。
代码清单 10-10(2):根据优先级将任务插入到就绪列表 pxReadyTasksLists[]。在这里插入图片描述

代码清单 10-11(1):创建空闲任务时,优先级配置为 tskIDLE_PRIORITY, 该宏在task.h 中定义,默认为 0,表示空闲任务的优先级为最低。
代码清单 10-11(2):刚刚我们已经修改了创建任务函数 xTaskCreateStatic(),在创建任务时,就已经将任务添加到了就绪列表, 这里将注释掉。
代码清单 10-11(3): 在刚刚修改的创建任务函数 xTaskCreateStatic()中,增加了将任务添加到就绪列表的函数 prvAddNewTaskToReadyList(),这里将注释掉。
在这里插入图片描述

将任务从就绪列表移除本应该完成两个操作: 1 个是将任务从就绪列表移除,由函数 uxListRemove()来实现; 另一个是根据优先级将优先级位图表 uxTopReadyPriority 中对应的位清零,由函数 taskRESET_READY_PRIORITY()来实现。但是鉴于我们目前的时基更新函数 xTaskIncrementTick 还是需要通过扫描就绪列表的任务来判断任务的延时时间是否到期,所以不能将任务从就绪列表移除。当我们在接下来的“任务延时列表的实现”章节中,会专门添加一个延时列表,到时延时的时候除了根据优先级将优先级位图表 uxTopReadyPriority 中对应的位清零外,还需要将任务从就绪列表移除。

在新的任务切换函数 vTaskSwitchContext()中,不再是手动的让 pxCurrentTCB 指针在任 务 1 、 任 务 2 和 空 闲 任 务 中 切 换 , 而 是 直 接 调 用 函 数taskSELECT_HIGHEST_PRIORITY_TASK()寻找到优先级最高的就绪任务的 TCB,然后更新到 pxCurrentTCB。
在这里插入图片描述

修改 xTaskIncrementTick()函数,即在原来的基础上增加:当任务延时时间到,将任务就绪的代码。
在这里插入图片描述
在这里插入图片描述

修改main函数
在这里插入图片描述

代码清单 10-15(1)和(3): 设置任务的优先级,数字优先级越高,逻辑优先级越高。
代码清单 10-15(2)和(4): 这部分代码删除,因为在任务创建函数 xTaskCreateStatic()中,已经调用函数 prvAddNewTaskToReadyList()将任务插入到了就绪列表。 。
在这里插入图片描述

实验效果,每20ms间隔 flag1 和 flag2都取反一次。
在这里插入图片描述

Hankin
2020.08.21

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值