embassy_preempt是一个嵌入式异步实时操作系统的调度模块。它通过Rust提供的协程机制,结合embassy的异步执行器的实现方式,并借鉴传统嵌入式实时操作系统uCOSII的任务切换机制,在任务调度时,若当前任务主动让权,则会进行栈复用;若当前任务被抢占,则会进行栈分配,并进行现场的保存,使得embassy_preempt同时具有了embassy低内存开销的优势以及uCOSII高实时性的特点。
在通常情况下,如uCOSII的嵌入式操作系统基本都以线程(或任务)为基本单位进行调度,这就使得每一个任务都将占有一个私有的栈空间。而在实际的应用中,在任务调度的过程中,大部分任务释放CPU都是由于主动让权,而非被高优先级的任务抢占,这使得栈空间存在一定的浪费。
而在embassy中,通过引入Rust的协程机制,使得栈空间的利用率得到了极大的提升。但是由于embassy的线程执行器中的协程之间无法进行抢占,并且进行协程调度时并没有优先级裁决机制,在多任务的情景下将导致若干个任务出现未被及时唤醒的情况,导致实时性较差,这是一个嵌入式实时操作系统无法接受的。
我们希望在已有的嵌入式实时操作系统的高实时性以及embassy的协程机制之间进行“折衷”,编写一个既可以满足实时应用环境下的实时性要求,又可以尽可能缩小内存使用的嵌入式异步实时操作系统调度模块——embassy_preempt。
执行器分成两个环节:thread模式下没有任务抢占的循环POLL环节和interrupt模式下发生任务抢占的interrupt POLL环节
thread的循环poll思路:
函数会直接从执行器里面取出highrdy任务调度执行,所以需要保证调用thread poll的时候是已经确定好了最高优先级的任务的,然后循环里面先poll任务执行,任务poll完后,一定是遇到pending或者任务完成退出,由于我们内置了时钟,这里就需要进行时钟的更新设置,然后就设置任务为非就绪等待后续的waker环节。最后找到新的最高优先级,继续循环
interrupt poll的思路:
中断POLL函数会进行抢占式的任务调度,在这里个函数里面主体会进行最高优先级是否有栈的检验,对于没有栈的最高优先级任务,它的调度执行我们会进行模拟压栈,让其变成一个有栈的任务,从而做到栈的分配(因为原有的栈在抢占调度的情况下会分配给当前执行任务用于上下文保存,那么新的任务执行一定需要额外的栈,要么是新任务本身就自带了,不然就需要额外分配一个)。然后我们会开始同一个的任务切换操作,这个操作被放置在pendsv可悬挂软中断里面,这是因为需要及时满足其它中断的需求,最后结束其它中断处理后才进行统一的任务切换处理。
这里说明一下,interrupt poll实际上只被我们暴露给用户的外部接口IntCtxSW调用,这个接口只是在调用interrupt poll之前进行了优先级判断,检查是否需要进行任务切换,如果需要就会调用interrupt poll
栈的分配过程是在中断过程中发生的(也就是抢占式调度),在发生中断并且需要抢占的时候,先设置当前任务状态是interrupt状态,表明当前任务被抢占,然后判断最高优先级任务(即将切换到的任务)是否有栈:
然后在实际任务切换环节,采用悬挂的pendsv软中断形式进行,避免重复无效任务切换,保证处理完紧急中断之后才进行任务切换。
栈的回收是在pendsv中断里面完成的,正好是顺着往下描述,
进入pendsv的时候就需要把现场进行保护,保存任务的上下文到psp栈
可以中断正在运行的任务,切换到另一个任务执行。嵌入式系统里通常是由于中断触发任务的调度,用户可以在实现中断驱动后,使用IntCtxSW调用操作系统的抢占调度操作