一、列表与列表项
二、队列
三、列表、与TCB里的列表项
1. 任务就绪列表:调度器切换任务时的目的列表。每个优先级一个任务就绪列表。2. 任务挂起就绪列表:调度器挂起时,如有任务准备就绪,只能先进任务挂起就绪列表。当调度器恢复时,任务从挂起就绪列表,移动到任务就绪列表。3. 任务挂起列表:被suspended的任务。4. 延时列表、溢出延时列表:当调用TaskDelay或者等待信号量有定义超时时间的,会放入延时列表。溢出延时列表,作为延时列表的扩展,当任务的延时唤醒时间溢出时,这个任务会被加入到溢出延时列表。当系统节拍计数溢出时,两个列表交换使用,原来的延时列表变为溢出延时列表,原来的溢出延时列表变为延时列表。※“任务状态列表项(左一)”,会被加入到以上的各种列表中,进行管理。
事件(或信号量)创建队列时,里面包含两个列表: TaskWaitToSend/Recv。※如果从事件队列中,请求(Take)事件,但事件队列当前为空,则这个任务的“事件状态列表项(左二)”就会被加入到“这个事件队列的” TaskWaitToRecv列表。当有其他任务或中断释放(Give)这个事件到队列时,会处理当前在等待这个队列的所有的任务。
四、延时列表的实现
freeRTOS延时列表相关的几个全局变量:1. xTickCount:无符号数字,标识运行后到现在的系统产生的tick数, 每个 tick发生时, xTickCount++,当xTickCount发生翻转时, pxDelayedTaskList和pxOverflowDelayedTaskList进行对调, Overflowlist变为正常的delay list。2. xNumOfOverflows:记录tick计数翻转的次数。3. uxTopReadyPriority:记录当前ready list中优先级最高的任务。4. xNextTaskUnblockTime:记录了延时任务列表中,第一个需要被唤醒的任务时间点。系统运行的每一个节拍,都会跟这个变量比对,如果满足任务延时完成, 则把这个任务放入就绪列表,如果这个任务比当前任务优先级高,就置位PendSV,立即进行任务切换。
为什么有两个延时列表?当TickCnt没有溢出时,唤醒的时间点 > TickCnt,就有两种情况,溢出或者没溢出。@唤醒时间点没有溢出的任务,将他们放到延时任务列表中,一旦TickCnt == 唤醒时间点时,就出队了。唤醒时间点溢出的任务,将他们放到溢出延时任务列表,当TickCnt也溢出了,把这两个列表交换一下,这时原来的延时任务列表肯定是空的了!因为TickCnt都溢出了啊!所以原来的溢出延时任务列表,变成了当前使用的延时任务列表。又可以参照@处的步骤走了。相当于溢出延时任务列表时钟是一个当前延时任务列表的拓展,互相交替使用,无穷尽。
五、任务调度的实现
freeRTOS任务调度相关的全局变量:1. uxTopReadyPriority:记录当前ready list中优先级最高的任务。2. pxCurrentTCB:记录现在运行的任务。
方法1:(1)每次创建任务时都会判断新任务的优先级是否大于 uxTopReadyPriority,如果大于的话就将这个新任务的优先级赋值给变量 uxTopReadyPriority。函数 prvAddTaskToReadyList() 也会修改这个值,将某个任务添加到就绪列表中的时候,用 uxTopReadyPriority 来记录就绪列表中的最高优先级。任务调度时,就从这个最高优先级开始判断,看看哪个优先级的任务就绪列表不为空就说明哪个优先级有就绪的任务。(2)已经找到了有就绪任务的优先级了, 接下来就是从对应的列表中找出下一个要运行的任务,查找方法就是使用函数 listGET_OWNER_OF_NEXT_ENTRY()来获取列表中的下一个列表项,然后将获取到的列表项所对应的任务控制块赋值给pxCurrentTCB,这样我们就确定了下一个要运行的任务了。可以看出通用方法是完全通过 C 语言来实现的,肯定适用于不同的芯片和平台, 而且对于任务数量没有限制, 但是效率肯定相对于使用硬件方法的要低很多。
方法2:使用硬件方法的时候 uxTopReadyPriority 就不代表处于就绪态的最高优先级了,而是使用每个 bit 代表一个优先级, bit0 代表优先级 0, bit31 就代表优先级 31,当某个优先级有就绪任务的话就将其对应的 bit 置 1。从这里就可以看出,如果使用硬件方法的话最多只能有 32 个优先级。__clz(uxReadyPriorities)就是计算 uxReadyPriorities 的前导零个数(汇编指令CLZ)得到 uxTopReadyPriority 的前导零个数以后在用 31 减去这个前导零个数,得到的就是处于就绪态的最高优先级了。比如优先级 30 为此时的处于就绪态的最高优先级, 30 的前导零个数为1,那么 31-1=30,得到处于就绪态的最高优先级为 30。可以看出硬件方法借助一个指令就可以快速的获取处于就绪态的最高优先级,但是会限制任务的优先级数。
六、其他。
没有获取到信号量,而且没有超时时间,获取信号量的函数会直接返回errQueue_Empty的错误值。
任务的状态不会改变,也不会加入到事件队列的等待列表当中
双加:
没有获取到信号量,但是有超时等待时间,这个任务的状态列表项被加入到延时列表,事件列表项被加入到事件队列的等待列表。
双踢:
当事件队列接收到事件,会逐个把这些等待中任务的事件列表项,踢掉。同时从延时列表中踢掉!
但是如果延时列表处理时发现一个任务的超时时间到了,会直接把他从延时列表踢掉,放入到就绪列表。还会查看他有没有在事件队列的等待列表中,如果有,一并从里面踢掉。
freeRTOS就是通过管理这些列表,来管理任务的。
在就绪列表的任务才会被调度,进行任务切换。
其他列表的任务则不会。
啊