1 优先级算法
首先明确一个概念。在中断中,配置的优先级数越小,则优先级越高,在任务中,配置的优先级数越小,任务的优先级就越低。在上一章中,空闲任务的优先级为0,其被加入了就绪列表的第0个元素中,其优先级越低。
FreeRTOS中查找最高优先级的算法有两种。
普通算法
它的思路就是从高优先级到低优先级判断各就绪列表是不是为空,不为空说明有TCB,则执行这个任务,并更新当前的最高优先级。uxTopReadyPriority 存的是就绪任务的最高优先级,最开始是0,taskRECORD_READY_PRIORITY(形参)这个函数用于更新uxTopReadyPriority,传入的形参比uxTopReadyPriority这个大,就会更新uxTopReadyPriority。
优化算法
就不用循环访问就绪列表了,得益于有个前导0的汇编指令 CLZ,这个指令可以计算从最高位开始出现了多少个连续的0,此时的uxTopReadyPriority就看成一个32位的图,其某个位置置1就表示有任务在这个优先级。因此只需要计算前导0的个数,然后用32减去这个个数,就能快速得到最高优先级,就去相应的就绪列表找TCB,并更新到当前的TCB,但是这样任务的优先级最多32个。
2 支持多优先级
第一步,需要在TCB中加入新的成员。同时在静态任务创建函数中也需要加入这个形参,这使得这静态创建任务里面内嵌的初始化新任务函数也要修改,同时新增一个将任务添加到就绪列表的函数(这个函数最开始是在主函数中调用的把任务加入就绪列表,手动添加的,可以去前面几章的实验部分看,现在内嵌到这个静态任务创建函数中),在创建动态任务的时候,也应该是在这里将任务加入就绪列表。
第二步 修改xPortStartScheduler()函数
因为在这个函数中调用了静态任务创建函数去创建空闲任务,相应的要在该函数中修改静态创建函数的传入形参。
第三步 修改vTaskDelay函数,
首先明确,当高优先级任务不主动提出阻塞,低任务是不会得到执行。
所以在这个主动提出延时的函数中,只需要把uxTopReadyPriority这个位图中相应的优先级位清0,就可以实现多优先级,但是当一个优先级下有多个任务的时候就不能清0,在这章中好像还没有考虑这个问题。这样其实就已经可以实现多优先级了,就算更高优先级的就绪列表中有节点,因为uxTopReadyPriority中相应的为清0,优化算法中前导0计算结果也不会去执行这个优先级更高的(只能使用优化算法)。在这个函数的结尾当然需要执行一次任务切换函数。
第四步 修改 vTaskSwitchContext( ) 也就是寻找最高优先级的TCB函数
其中只使用这个函数,taskSELECT_HIGHEST_PRIORITY_TASK();这个函数根据宏定义选择普通方法还是优化方法。
第五步 修改Systick的时基函数
回想上一章中的延时阻塞如何到期切换的。在Systick中断函数中把每个任务的TCB中的延时时间减1,然后去调用任务切换函数,触发PendSv中断,中断函数中再调用这个函数vTaskSwitchContext( ),vTaskSwitchContext 函数去查看各任务的延时时间是不是为0,如果为0,就去执行这个为0的任务,都不为0,就去执行空闲任务。
而在这章中,vTaskSwitchContext( )函数已经变了,它是根据优化算法,前导0的个数来计算应该执行哪个优先级下的任务。因此在Systick的中断时基函数中,把延时到期的任务的优先级加入到这个uxTopReadyPriority位图中,其具体调用了taskRECORD_READY_PRIORITY()这个函数,在85页可以看到,如果这个计时到期的任务的优先级高于uxTopReadyPriority,则把uxTopReadyPriority变成这个到期的任务的任务,如果小于,则不更改,毕竟到期了任务的优先级低,任务不需要切换(此时任务的TCB是存在就绪列表中的,一直都在)。
至此,多优先级就可以实现了。