合作式调度器与抢占式调度器
(2018-01-23 10:17:18)分类: 嵌入式 |
对于单片机开发,比较常用的一种程序执行模式为超级循环模式,即将需要完成的任务构建成一个或多个函数然后在一个永不退出的死循环中一直运行,代码类似如此:
while(1)
{
Function_1();
Function_2();
.
.
.
Function_x();
}
使用这种结构加上各种中断可以应对大多数的要求,工程师也可以清晰的了解代码运行过程,但某些应用要求使用这种结构在实现上比较困难,比如说任务
1,要求每隔10ms采集一次传感器数据,
2, 每隔500ms要发送一次数据个另一个设备,同时,由于通讯采用某种协议,协议内数据帧又有通讯超时的要求。
这种对时间上有要求的任务可能不止一个,原来是超级循环模式在时间上就很难控制了。
这种情况就可以使用一种基于时间触发的调度器算法,来完成有时间要求的任务。
关于在单片机上实现调度器的算法,在《时间触发嵌入式系统设计模式》这本书里进行了详细的阐述,这篇文章是阅读了这本书的一些笔记。
一,抢占式调度器
在这种模式下调度器会在固定的间隔时间查询是否有比当前任务优先级更高或同等级的任务需要被执行,当查询到存在的时候,不管当前任务是否执行完成,都会进行任务切换,否则将继续执行当前任务。
这种模式要求在任务切换时进行上下文保存和装载,这增加了处理器的开销。
二,合作式调度器
合作式调度器会在当前任务执行完成再执行其他任务,超级循环模式本质上也是一种合作式任务模式,但如之前所述,循环模式对于某些任务在实现上比较困难,在《时间触发嵌入式系统设计模式》这本书里描述了一种基于时间触发的合作式调度器,这种软件结构依然是在任务执行完成后才会执行令外需要被执行的任务,任务不会被其他任务中断,但是加上了时间间隔触发机制后,对于有执行时间要求的任务依然可以准确的执行,并且由于合作式调度器在进行任务切换时不需要进行上下文保存和转载,这样就只需要很少的CPU开销。
这种模式下的任务存在两种状态,运行、等待。
实现时间触发的合作式调度器需要的一个硬件定时器用来实现隔间中断,在中断中更新任务状态,
调度器需要实现的函数如下:
1,hSCH_Start()
调度器初始化函数,最简单的实现就是初始化一个定时器.
2,hSCH_Add_Task(func_name, delay, Period)
将任务指针添加到调度器的任务表中,任务表最简单的实现方式就是一个数组。
3,SCH_Delete_Task(Func_Handle)
如果任务只执行一次,在执行后就可以将其从任务表中删除
4, hSCH_UpData(void)
任务状态更新函数,通常就是定时器的中断例程函数,在函数中根据任务执行周期更新任务执行状态
5,hSCH_Dispatch_Tasks()
任务调度器,此函数是在main函数while(1)中执行的函数。主要是查询任务表中有没有已经准备好待执行的任务,如果有就执行任务,实现任务的调度。
这种模式的任务表数据结构也非常坚持,一个只需要8个处理器宽度的内存。
相比来说,在某些任务要求下,使用这种合作式的调度器比起使用更为普遍的uCOS或Free_RTOS可以节省更多的硬件资源。
三,混合式调度器
书中还介绍了一种混合式调度器,是在合作式调度器基础上允许添加一个抢占式的任务,此任务可以中断其他任务,这任务会有严格的执行时间要求
while(1)
{
}
使用这种结构加上各种中断可以应对大多数的要求,工程师也可以清晰的了解代码运行过程,但某些应用要求使用这种结构在实现上比较困难,比如说任务
1,要求每隔10ms采集一次传感器数据,
2, 每隔500ms要发送一次数据个另一个设备,同时,由于通讯采用某种协议,协议内数据帧又有通讯超时的要求。
这种对时间上有要求的任务可能不止一个,原来是超级循环模式在时间上就很难控制了。
这种情况就可以使用一种基于时间触发的调度器算法,来完成有时间要求的任务。
关于在单片机上实现调度器的算法,在《时间触发嵌入式系统设计模式》这本书里进行了详细的阐述,这篇文章是阅读了这本书的一些笔记。
一,抢占式调度器
二,合作式调度器