2015-04-27 17:17
什么是多任务:
通过频繁的任务切换来保证在一段时间内,每个任务都能得到合适的运行时间。在任务调度器中,可以通过标志任务的重要性,使有实时要求的任务得以优先运行。
任务的切换:保存现场(将SP指向任务的私有堆栈,并压入CPU的重要寄存器)后,修改SP指针,通过RET或者RETI指令将预先保存好的指令地址弹到PC寄存器,从而改变程序运行的地址。
1.任务在运行时,SP一直指向私有栈,所以在切入调度器时,运行地址已经被自动压入堆栈。如果是中断程序,还会自动保存CPU现场。
2.在Main函数完成系统初始化后,需要有一个空闲任务(Task0)来占领CPU的空闲时间。
3.任务的切换有两种调度器——中断调度(主要是时间片调度)和程序调度(强行调度)
系统有两条任务队列:普通任务队列(TaskQueue)和优先任务队列(TaskQueue_Pri)
当有优先任务存在时,会一直运行优先队列里的任务。所以在删除任务(包括任务进入延时或者等待消息)的时候,如果队列为空了,必须要第一时间修改系统标志位。在Trim后,不必修改。
系统标识结构体内容:
uchar TaskID; //当前任务号
uchar SystemState; //系统运行状态标志 on off
uchar NewTaskID; //新任务号
uchar TimeBlock; //任务运行时间片寄存器
uchar SchLock; //调度器锁
uchar IntNesting; //中断嵌套状态
uchar SystemMode; //系统运行模式
uchar SystemPri; //优先运行状态
任务标志结构体内容:
uchar TaskSP; //任务栈顶地址
uint TaskDelay; //任务延时寄存器
bit TaskPri; //标志一个任务是目前否是优先任务
uchar TaskState; //任务状态标志 run stop delay
uchar TaskLastState; //任务被挂起前的状态
任务切换过程:
1.到点(任务运行状态、时间等因素),进入临界状态。
2.检查系统是否存在优先队列(SystemPri)。
3.从相应的队列中取出任务(GetTaskID),这个函数会自动Trim队列以保持队列的完整性。被取出的新任务号放入系统标志结构体中的NewTaskID,准备进入切换过程。到此为止任务切换已经进入必然,不可能撤销了。
4.将当前的现场压入堆栈(在中断调度器中就不需要了,已经被系统自动压栈了)。完成后将目前的SP地址保存在任务标志结构体中的TaskSP中等待下次调用。
5.修改系统标志结构体,正式将NewTaskID放入TaskID中,NewTaskID清零。
6.在新任务的标志结构体中读取TaskSP地址,并将上一次保存好的现场弹出到各个寄存器。
7.通过RET或RETI结束调度进入新的程序地址。
在一个任务中使用调度函数,在调度函数结束后会自动进入新任务而不是原来的断点,原任务的内容在调度函数内会进行妥善保存。
任务注册的过程:
1.任务具有唯一性,每个任务只能单独运行一个线程。
2.根据任务的需求,在结构体内设置其优先级
任务延时:
1.任务调用延时函数
2.进入延时函数后,系统进入临界状态。在临界状态下,保存当前的现场。
3.将延时的时间存入任务控制块的TaskDelay,并改变任务的运行状态为Delay状态。
4.调用调度器函数调入新任务,被延时的任务则不需要加入就绪任务队列。
如果任务数量很多,可以另外建立一个延时任务队列。