一个多任务的操作系统可以同时运行多个任务,在只有一个CPU的情况下,怎么同时运行多个任务呢?其实就是操作系统控制CPU在多个任务之间来回切换,这种切换对于任务来说,可以是主动发起的(休眠、阻塞),也可以是不可感知的(优先级抢占、时间片流失)。由于切换的频率极高,速度极快,给用户的感觉就好像是同时在运行多个任务一样。那么,CPU是怎么在任务之间切换的呢?我们知道CPU运行程序的所有局部变量、需要重复使用的寄存器信息以及返回地址等都保存在堆栈中。特别是发生中断时,通用寄存器信息入栈、返回地址入栈,当中断处理完成后,再从堆栈中装入这些寄存器信息,并获得返回地址,返回断点后继续执行,这样就完成了中断的处理,并且不会影响到程序的原有执行流程。但是,如果我们在中断处理完成后,从堆栈中装入另一个任务的寄存器信息和返回地址,这样是不是就可以达到任务切换的效果呢?对!!原理基本上就是这样。
比如,任务主动发起切换:
任务A请求任务切换 -> 任务A寄存器入栈(保存断点) -> 保存堆栈指针到任务上下文-> 找到一个最高优先级的就绪任务 -> 从新的任务上下文获得堆栈指针 -> 装入新任务的寄存器信息 -> 返回新任务的断点继续执行。
优先级抢占切换(时间片轮转切换):
任务A正在执行时发生硬件中断 -> 任务A寄存器入栈(保存断点) -> 保存堆栈指针到任务上下文-> 中断服务程序唤醒了高优先级的任务B-> 从任务B的上下文获得堆栈指针 ->装入任务B的寄存器信息 -> 返回任务B断点继续执行
通过上面的分析,我们已经了解了操作系统任务切换的基本原理,接下来我们可以开始实现我们的任务切换代码了。
SysTick_Handler PROCCPSID I ; Why to disable IRQ ? Guess !PUSH {R0, LR} ; Why to push R0?BL CORE_EnterIRQ ; Set current interrupt nest layerBL CORE_TickHandler ; Inc the system tickCPSIE I ;BL CORE_TaskScheduling ; Find the new task will be schedulingCPSID I ;POP {R0} ; Stack must be aligned to 64 bit, so push R0BL CORE_LeaveIRQ ; Set and get current interrupt nest layerCBNZ R0, ST_LE ; Nest layer != 0 then leave this interruptBL CORE_CheckMustbeSchedule ; Check need scheduleCBZ R0, ST_LE ; Must schedule = FALSE then leave this interruptPUSH {R4-R12} ; nest layer = 0 and Must schedule = TRUE then schedulingMRS R0, MSP ; R0 = Co