前言
TwinCAT3 支持多核心CPU并行运行实时任务,根据官方网站的帮助信息“实时”定义取自DIN44300,而且实时任务的调度算法默认是 RMS算法(速率单调调度算法)
RMS算法
来看一下百度百科的解释:
RMS(单调速率调度算法)是一种静态优先级调度算法,是经典的周期性任务调度算法。 RMS的基本思路是任务的优先级与它的周期表现为单调函数的关系,任务的周期越短,优先级越高;任务的周期越长,优先级越低。如果存在一种基于静态优先级的调度顺序,使得每个任务都能在其期限时间内完成,那么RMS算法总能找到这样的一种可行的统调度方案。
理论证明过程可以参看百度百科上的论述,这里不细说,直接给结论:
1)单核心处理器处理周期性任务时,RMS算法是最优的;
2)RMS算法是抢占式算法,周期越短的任务,优先级越高。
针对第二点,例如对于处理器来说,假定有2个任务,周期分别是 任务A:10 ms,任务B:50ms,那么CPU每50ms就会“中断”一次去执行任务A,A必须确保在10ms内执行完,假如A实际执行仅用2ms,那么剩下的8ms就可以给任务B执行,如果任务B实际执行需要35ms,那么执行完8ms以后,又到了10ms的中断时机,这时候CPU就把任务B挂起,把它的当前状态保存起来,以备后续接着执行任务B,转头去第二次执行任务A,任务A又用了2ms,这时候又空出来8ms来执行任务B,任务B再次执行8ms,CPU被迫中断,到目前为止,任务B总共运行了16ms,任务A运行了4ms,CPU运行了20ms。第三轮抢占再次开始,CPU第三次运行时,任务A占去2ms,任务B占去8ms,10ms时间又到了,CPU再次被中断,此时任务A运行了23=6ms,任务B总共运行了83=24ms,CPU总共运行了103=30ms;第四次抢占开始,任务A运行2ms,任务B运行8ms,CPU运行10ms,然后再次中断,到目前为止,任务A运行了24=8ms,任务B运行了 84=32ms,CPU运行了 104=40ms;第五次抢占开始,任务A运行2ms,任务B运行8ms,CPU运行10ms,任务A总共运行了25=10ms,这10ms是真正被任务A利用的CPU时间,CPU没有空转浪费。任务B运行3ms,这时,任务B总共运行了 32+3=35ms,才算是跑完了完整的“一圈”,期间被任务A多次打断。这时候CPU还剩 10-2-3=5ms空余时间,但是!!!!因为任务B的周期是50ms,所以CPU前四次总共执行了104=40ms,第五次实际跑任务A和任务B又花费了5ms,下一个10ms计时到,任务A的10ms中断和任务B的50ms周期中断一起触发,所以CPU空余时间实际上只有50-45=5ms,第六轮开始…
可以发现,在CPU运行的50ms时间内,任务A的代码被跑了5遍,任务B的代码只跑了1遍。
中断机制
Double-Tick中断
由于TwinCAT3支持多核心运行,衍生出两种核心分配机制即共享核心和独占核心,共享核心由TwinCAT实时调度器(下称 TwinCAT kernel)分配实时任务和Windows系统的占比,默认是 80%,即CPU在预设的实时周期中,其确保80%的CPU运算时间分配给实时任务,Windows系统只使用剩下的20%运算时间,这个值是可调的,如果接触过FreeRTOS这类tiny real-time kernel的人可能会注意到,在FreeRTOS中task的切换需要保存当前正在运行task的上下文,如果没有运行完,下一次排到它的时候,它就可以恢复当时的上下文环境(本质是寄存器中的值)接续运行。在TwinCAT的调度机制中,共享核心也需要在实时任务和非实时任务之前切换,若当前周期内实时任务没有运行完,那么就需要保存实时任务的上下文环境,以备下一周期恢复来接续执行。80%的设定就是保证在共享核心中,Windows系统总能获得20%的时间来执行,这个“切换点”就是中断来控制的,TwinCAT中称之为Double Tick。
下图中,第一个周期内,Double-Tick到来时实时任务还没有运行完,这时就需要保存实时任务的上下文环境,第二个Base Time到来的时候,上一次没有执行完的任务又获得了一段执行时间(绿色和红色的部分),然后才轮到了本次的“读输入-执行代码-写输出”的实时任务。
Tick中断
Tick中断是用来规划CPU时间片的,例如有两个核心的CPU,其中一个核心运行了PLC Task,该PLC Task的运行周期是10ms,那么每10ms中断一次就是Tick中断在负责。注意这里的Base Time就是10ms,如果在TwinCAT中把 Base Time设为100us,但是PLC Task设为 10ms,那么下图中的Base Time还是10ms,这个是要注意区分的。
在独占核心的情况下,Double-Tick中断没有存在的价值,因为整个核心被用来跑实时任务,也就是说TwinCAT Real-Time Kernel独占这个核心,Windows操作系统不能使用这个核心来运行了,那么实时任务的调度就变得简单了,如下图所示,这样用户的PLC程序获得了更过的CPU时间,用来跑一些计算量大的程序或者对jitter要求严苛的程序适合使用独占核心的这种配置方法,jitter会在Double-Tick的时候产生,由于和用户程序相关这个jitter存在“抖动”。
超时机制
对于用户而言,预测自己程序的执行时间非常困难,尤其是目前多核心的CPU,结合不同的核心分配机制,这个时间是不确定的,那么一旦用户程序写的非常复杂,在一个周期内并不能运行完毕该怎么办,TwinCAT的处理方法是放到下一个周期中继续运行,这样实际上挤压了下一个周期中程序的执行时间,导致后面每个周期都不能来运行本属于自己这个周期的程序,反而得腾挪时间来跑上一次积压下来的程序,这样的话,实际上几轮周期下来,对应关系就乱起来了,而且程序实际跑了几圈并不能通过周期运行圈数来一一对应,这种情况其实是不允许的。
如下图所示,CPU跑了4圈,但程序实际上只执行了3次,这种情况就会导致超时计数器累加。
一个正常运行的程序,超时计数器不应累计,应当始终为零,如下图所示