资源描述:
《51简单操作系统OS》由会员上传分享,免费在线阅读,更多相关内容在工程资料-天天文库。
1、当然,这里要申明一下,这玩意儿其实算不上真正的操作系统,它除了并行多任务并行外根本没有别的功能.但凡事都从简单开始,搞懂了它,就能根据应用需求,将它扩展成一个真正的操作系统.好了,代码来了.将下面的代码直接放到KEIL里编译,在每个task?()函数的"task_switch();"那里打上断点,就可以看到它们的确是"同时"在执行的.#include #define MAX_TASKS 2 //任务槽个数.必须和实际任务数一至#define MAX_TASK_DEP 12 //最大栈深.最低不得少于2个,保守值为12.unsigned char idat
2、a task_stack[MAX_TASKS][MAX_TASK_DEP];//任务堆栈.unsigned char task_id; //当前活动任务号//任务切换函数(任务调度器)void task_switch(){ task_sp[task_id] = SP; if(++task_id == MAX_TASKS) task_id = 0; SP = task_sp[task_id];}//任务装入函数.将指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中原来就有任务,则原任务丢失,但系统本身不
3、会发生错误.void task_load(unsigned int fn, unsigned char tid){ task_sp[tid] = task_stack[tid] + 1; task_stack[tid][0] = (unsigned int)fn & 0xff; task_stack[tid][1] = (unsigned int)fn >> 8;}//从指定的任务开始运行任务调度.调用该宏后,将永不返回.#define os_start(tid) {task_id = tid,SP = task_sp[tid];return;}
4、/*============================以下为测试代码============================*/void task1(){ static unsigned char i; while(1){ i++; task_switch();//编译后在这里打上断点 }}void task2(){ static unsigned char j; while(1){ j+=2;
5、 task_switch();//编译后在这里打上断点 }}void main(){ //这里装载了两个任务,因此在定义MAX_TASKS时也必须定义为2 task_load(task1, 0);//将task1函数装入0号槽 task_load(task2, 1);//将task2函数装入1号槽 os_start(0);}限于篇幅我已经将代码作了简化,并删掉了大部分注释,大家可以直接下载源码包,里面完整的注解,并带KEIL工程文件,断点也打好了,直接按ctrl+f5就行了.现在来看看这个多任务系统的原理:这个多任务系
6、统准确来说,叫作"协同式多任务".所谓"协同式",指的是当一个任务持续运行而不释放资源时,其它任务是没有任何机会和方式获得运行机会,除非该任务主动释放CPU.在本例里,释放CPU是靠task_switch()来完成的.task_switch()函数是一个很特殊的函数,我们可以称它为"任务切换器".要清楚任务是如何切换的,首先要回顾一下堆栈的相关知识.有个很简单的问题,因为它太简单了,所以相信大家都没留意过:我们知道,不论是CALL还是JMP,都是将当前的程序流打断,请问CALL和JMP的区别是什么?你会说:CALL可以RET,JMP不行.没错,但原因是啥呢?为啥CALL过去的就可以用RE
7、T跳回来,JMP过去的就不能用RET来跳回呢?很显然,CALL通过某种方法保存了打断前的某些信息,而在返回断点前执行的RET指令,就是用于取回这些信息.不用多说,大家都知道,"某些信息"就是PC指针,而"某种方法"就是压栈.很幸运,在51里,堆栈及堆栈指针都是可被任意修改的,只要你不怕死.那么假如在执行RET前将堆栈修改一下会如何?往下看:当程序执行CALL后,在子程序里将堆栈刚才压入的断点地址清除掉,并将一个函数的地址压入,那么执