再议基于μC/OS-II的时间片调度法设计
扫描二维码
随时随地手机看文章
引言
笔者曾在本刊2008年12期上发表过一篇文章——《基于μC/OS-11的时间片调度法设计》,近期在网络上看到有很多感兴趣的技术同行作了大量引用和转载。根据在实际项目中的应用经验,有必要对其再议,主要原因如下:
①多任务的时间片调度在嵌入式领域有实用价值。一方面是很多嵌入式软件系统升级有这种需求,旧的软件模块基于Endless Loop实现,升级到μC/OS-II后,若要最大限度地复用旧的软件模块,时间片调度算法是实现旧的设计模式到新架构之间最简单的桥梁。另一方面,对于控制领域,存在大量的耗时任务无法自动释放控制权,时间片调度降低了任务设计的复杂度。刚刚发布不久的μC/OS-II,推出的重大改进之一就是增加了对Round Robin的支持,更是表明μC/OS-II的使用者们对该功能的实际需求是切实存在的。
②不更改μC/OS-II内核代码实现时间片调度。《基于μC/OS-II的时间片调度法设计》对OS内核代码作了修改,虽然很少但增加了系统耦合度,对日后的项目维护和第三方升级都是不利的。如果存在完全不用更改内核而仅基于OS服务的正常调用的实现方案,对系统的可靠性和移植性都是有益的。
③相对μC/OS-III,μC/OS-II仍然有广泛的应用领域。μC/OS-III作出了重大的改进,增加了时间片调度,扩展了任务数的限制,允许了同等优先级任务的存在,更多新特性的引入带来了内核结构的重整以及运行时开销的增加。虽然可以通过配置优化系统,但就大部分的应用而言,μC/OS-II完全能够胜任,至少不需要仅仅为了时间片调度功能而选用μC/OS-III。
1 调度原理
该调度算法对系统的要求有两点:首先要建立一个额外的调度任务用于管理待调度的用户任务时间片计时及其切换,我们将其命名为TaskRB_Scheduler;其次是保证其任务优先级高于所有的待调度时间片任务,保证调度任务能抢占所有被调度任务的控制权。
我们假设系统中有3个任务需要分享时间片,分别为TaskRB_1、TaskRB_2、TaskRB_3。为了实现时间片的调度功能,还需要额外的调度任务TaskRB Scheduler,于是系统中的将会有4个任务。其中,TaskRB Scheduler由初始化代码创建,而待调度的3个用户任务的创建和管理完全由TaskRB Scheduler任务来完成。
TaskRB_Scheduler的运行分为两个阶段:首先是初始化阶段,负责所有时间片任务的创立并确保其处于Suspend状态;然后是调度运行阶段,如图1所示。
下面对照图1对调度运行阶段的各个步骤进行描述。
①TaskRB_Scheduler通过调用OSTaskResume使TaskRB_1处于Ready状态(其他任务处于Suspend态)。
②TaskRB_Scheduler调用OSTimeDly释放控制权进入延时等待状态,延时参数即为TaskRB_1任务运行的时间片长度,此时唯一处于Ready态的用户任务TaskRB_1获得控制权进入Running状态。
③OSTimeDly超时发生,TaskRB_Schedulcr处于Rcady态并且由于任务优先级高于TaskRB_1抢占其控制权,于是TaskRB_Scheduler进入运行状态,TaskRB_1返回Ready态。
④TaskRB_Scheduler调用OSTaskSuspend让处于Ready态的TaskRB_1进入Suspend状态,避免其竞争下一个时间片。
⑤~⑧重复①~④步骤,对TaskRB_2分配运行时间片。
⑨~重复①~④步骤,对TaskRB_3分配运行时间片。
简而言之,调度任务因为具有最高优先级,可以根据时间片分配表启动并打断待调度的时间片任务,虽然时间片任务优先级各不相同,但通过确保仅有一个时间片任务处于Readsr态的方法,避免了不同时间片任务之间的竞争
冲突。
为简化示意图,并没有标注中断ISR行为的影响,事实上ISR只能影响到任务时间片的长度而不会影响到任务片调度的流程;而对于时间片调度系统而言,通常都采用类似于前后台系统的信号同步策略处理中断数据,这已经不是本文讨论的重点。
2 实现代码
原理比较简单,仅仅给出 TaskRB_Scheduler部分代码:
3 应用扩展
有项目经验的人都深知Demo和产品有着天壤之别,这句话同样适用于此时间片调度方案。在实际应用中,我们还须关注到以下几个方面。
(1)如何与非时间片调度任务协同工作
对于混合系统而言,作为时间片调度的任务通常都对实时性要求比较低,因此通常的做法是设置优先级:实时任务优先级>TaskRB_Sche duler>时间片任务>IDLE,时间片精度会会因为实时任务的抢占受到影响。
(2)如何处理时间片任务中的critical_section
critical section的处理很重要,我们一般不希望一个时间片任务在发送UART数据包的过程中被TaskRB_Scheduler暂时掐断,但TaskRB_ Scheduler又的确无法预知时间片用户的状态。有两种解决办法:一是通过调度器上锁,二是通过时间片任务和调度任务共享互斥量。显然,调度器上锁不是个好主意,会封锁正常的任务切换,在上述的混合系统中简直是不可想象的,但在纯粹的时间片调度系统中不会带来太多的麻烦。共享互斥量增加了系统资源的消耗,在混合系统中的运行时效率更高。
(3)运行时配置
TaskRB_Scheduler一方面是时间片的调度器,另一方面也是一个普通的μC/OS-II任务,在实际项目中可以通过创建任务链表的方式维护管理纳入时间片调度的用户任务,在运行时灵活地添加删除任务列表以及调整时间片宽度。鉴于原理都很简单,实现代码不再赘述。