完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
系统内核对外提供一些接口,主要有系统初始化,系统开启,任务创建及任务延时等。其功能原型如下:
void OS_Init(void); void OS_Start(void); void Task_Creak(void (*pfun)(void),INT8U *pStack,INT8U Task_ID); void OSTimeDly(INT8U ticks); 1任务入口地址: 在程序中,对某个功能函数进行调用时,一般会采用【程序名(参数)】这种方式进行调用,如下: int add(int a, int b) { return a +b; } int main() { sum = add(1,2);//调用函数 return 0; } 而在OS中,是不直接采用【程序名(参数)】这种方式调用任务函数的,而是对传统函数调用在底层上的模拟(想了很久都没有找到合适的话来解释,如果我没讲明白,可以看接下来的具体代码解释)。了解过C语言的函数调用原理或者编译原理的同学都知道,程序有一个运行时结构,当在调用函数时,程序会终止对主调函数(对应上面例子中的main()函数)的执行,转而去执行被调函数(对应上面的例子的add()函数),于是变会将被调函数的入口地址赋值给PC指针(编译器的工作),而之前,程序会对被调函数执行完后的返回地址进行压栈(编译器的工作),当被调函数执行完后,该值会赋给PC指针(编译器的工作),以便继续执行中断的主调函数,不过由于退栈的缘故,被调函数的运行环境会在被调函数执行完毕后销毁掉。这也对应了操作系统原理中所说的【上下文环境的切换】,当然也不完全相同,只是类似,操作系统的上下文切换是由操作系统控制的,本质是对上述行为的模拟,不是由编译器控制的,再者上述的调用过程会销毁被调函数的运行环境,而操作系统的目的是要在多个任务函数上进行连续切换执行,因此需要对各任务函数的运行环境进行保存,这就是下面将讲到的任务私有堆栈的工作。 其实在C语言中(包括大部分语言)函数的函数名就表示函数的入口地址,该地址和变量地址一样需要使用一个指针变量进行存储,这就是所说的函数指针。上面的第一个例子可以改为: typedef int(*FUN)(int a, int b);//定义一个合适的函数指针类型 int add(int a, int b) { return a +b; } int main() { FUN ADD = add; sum = ADD (1,2);//调用函数 return 0; } 为了简便介绍,默认的任务函数为无参无返回值函数(当然,为了使任务函数更加灵活,也可以为任务函数进行传参,如果支持传参,需要在汇编部分将参数赋值给相应的寄存器),一个简单的任务函数。 typedef int(*TASKFUN)(void);//定义一个任务函数类型 void task1(void)//写一个任务函数,task1便是入口地址 { while(1)//任务函数必须是死循环函数,不然会跑飞 { /*任务代码*/ OSTimeDly(100);//我们写的是一个实时操作系统,不是分时系统,因此需要主动交出执行权限,该函数会在后面给出 } } 任务函数必须是死循环函数,因为写一个简单入门OS,不支持销毁销毁任务,想了解更加完善的OS可以看UCOS的源码,我也是一开始写了一个简单的OS后去看UCOS,会发现UCOS的最基本的内核和我们写的是差不多的,无非是功能更加完善。我也尝试对UCOS进行了相应裁剪工作(将一些不常用的功能剔除,或者将一些功能简化)并可正常运行,我一直都是围绕着这个简单的OS内核模型来思考的,因此我觉得写一个简单的OS模型还是有好处的,可以在用一些成熟的操作系统的时候,面对庞大的源码,也不至于思维混乱。即使在学习Linux这种分时操作系统也时有所帮助。 2任务私有堆栈: 上面已经提到,操作系统的目的是要在多个任务函数上进行连续切换执行,因此需要对各任务函数的运行环境进行保存,因此需要为每个任务必须有属于自己的堆栈,而对运行环境的保存的数据结构就是任务私有堆栈,也称为人工堆栈。人工堆栈大小需要谨慎选择,不能短也不能太长,短了会是溢出会可能修改其他任务的人工堆栈,产生调度紊乱。太长会浪费空间,尤其是像51这种硬件资源本就少的单片机。在这里,堆栈的空间的预留是通过数组来划分的,即静态分配,当然也可以采用动态分配。在建立任务时,要对堆栈初始化(这也很关键),将任务入口地址压到最底部(不同的单片机情况不同,这里以51为例,后面的也是),然后sp指向正确的堆栈位置(不同的单片机情况不同,要保存的寄存器个数不同),个人在设计中发现,为了不让sp越界,最好将堆栈最底部单元预留出来,避免浪费可以用来保存任务信息,比如堆栈使用情况。 #define MAX_TASK 2 INT8U Stack[MAX_TASK][27]; 3任务控制块: 在操作系统中任务是以任务控制块来管理的,和人工堆栈一样每个任务也有属于自己的任务控制块。一旦任务被创建,系统便会为任务提供一个任务控制块,任务代码与任务控制块关联后便成为系统的一个进程。任务控制块是由结构体描述的,由于内核的精简,任务控制块只由两个成员组成,代码如下: typedef struct{ //INT8U OSTCBStkPtr; //当前TCB堆栈指针 INT8U OSTCBSP; //INT8U *OSTCBPREV; //前一个TCB指针 //INT8U *OSTCBStkNext;//后一个TCB指照 //INT8U OSTCBPSW;j // INT8U *OSTCBSP; // INT8U OSTCBPrio; //优先级 // INT8U OSTCBRdy; //就绪状态 INT8U OSTCBDly; //延时节拍 }OS_TCB; idata volatile OS_TCB os_tcb[MAX_TASK];//定义任务控制块表 根据系统需求成员定义不同(一些成熟的OS会利用预编译来确定需要的成员,比如当配置时关闭一些功能,利用预编译判断不加入一些成员变量,从而达到节约内存的效果),在这里,需求是自由延时服务的OS,只需要一个保存任务SP的成员变量和保存延时时间的成员变量,一些在一些成熟OS上常用的成员变量在这里可以注释掉。 5任务状态: 在操作系统中方便任务的管理,将任务分为不同的状态,为了精简系统,系统任务状态分为三种,如下图所示。 操作系统使用任务就绪表来记录任务的就绪情况,调度时从就绪表中获取优先级最高的就绪任务。 6 就绪表和查找表 利用就绪表和查找表可以在任务调度切换阶段,找到就绪表中优先级最高的任务,为节约数据空间,任务状态使用一个字节的位来储存任务的就绪状态,即就绪表为一个一字节变量,且位号代表优先级别,约定位0位最高级别,位7为最低状态。 volatile INT8U CurID; volatile INT8U HighRdvID; volatile INT8U OS_Task_List; //就绪表优先级由高到低 0最高位 INT8U code OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};//查找表 //查找最高优先级代码 for(i=0;i { if(OS_Task_List&OSMapTbl) { break; } } CurID=i;//找到最高优先级ID 这里的就绪表索引比较简单,仅仅是提供一个位掩码来对就绪表进行索引,但没有考虑到系统的实时性,无法在固定的已知时间内找到优先级最高的任务,而UCOS利用了更加高级的方式来对优先级最高任务进行查找。 /*就绪表*/ RLOS_U8 OSRdyTbl[RLOS_RDY_TBL_SIZE]; /*查找表*/ RLOS_U8 const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; RLOS_U8 const OSUnMapTbl[] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */ }; //查找最高优先级代码 RLOS_U8 y; y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run*/ OSPrioHighRdy = (RLOS_U8)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);找到最高优先级ID 具体为什么可以在固定时间找到最高优先级任务可以自行百度。还有需要注意的是,为了操作系统正常运行,必须保证任何时候都有任务可执行,也可以让系统默认设置一个优先级最低的默认任务。 7时钟节拍: 时钟节拍是系统的心脏,由硬件产生,51系列可以用定时器产生毫秒级定时中断。初始化代码如下: void StartTicker(void)// { TMOD=0x01; TH0=0x0d8; TL0=0x0f0; ET0=1; TR0=1; } //定义一个中断函数 void TIME_TICK() interrupt 1 { static INT8U OSTime;//系统时间节拍 TF0=0; EA=0; TH0=0x0d8; TL0=0x0f0; OSTime ++; TickInterrupt();//时钟中断处理事务,主要任务处理调度和休眠计时,会在后面讲到 } 8临界区: 由于操作系统是并发运行的,因此保证一些重要数据资源同步需要让系统停止调度,在这里让系统禁止多任务访问的代码片段称为临界区。 在这里使用的51单片机,让系统停止调度的直接了当的办法就是关闭中断进入临界区,开启中断离开临界区。 #define OS_ENTER_CRITICAL() EA=0 #define OS_EXIT_CRITICAL() EA=1 9系统延时: 通过系统延时,让当前任务主动交出执行权,并设置休眠时间。在钟中断处理事务函数中,会对休眠的任务进行休眠倒计时,当休眠时间结束时会重新加入就虚表中,然后参与任务调度。 void OSTimeDly(INT8U ticks) { S_ENTER_CRITICAL();//进入临界区 //os_tcb[CurID].OSTCBSP=SP; os_tcb[CurID].OSTCBDly=ticks; OS_Task_List&=~OSMapTbl[CurID]; OS_EXIT_CRITICAL();//退出临界区 OS_TASK_SW();//任务调度 } 10任务调度: 学过单片机原理的都知道,cpu中有sp与pc两个特殊的寄存器,sp是堆栈指针,在51中它可以指向数据区的任意单元,PC是程序计数器,它始终保存下一条程序指令的地址。51C语言是可以直接操控sp的,但是pc不行,所以要想办法间接操控pc,对的,就是通过压栈和弹栈实现,在程序执行发生断点时(调动子程序或中断),cpu会自动将pc的值进行压栈,返回断点时会自动将栈顶的值弹回pc,这就是关键,如果在返回前,我们修改sp,不就可以间接操控pc了吗!这样就可以将cpu执行其他任务了; 任务的调度也称上下文的切换,任务调度过程如下图所示。 |
|
|
|
|
|
只有小组成员才能发言,加入小组>>
3263 浏览 9 评论
2944 浏览 16 评论
3443 浏览 1 评论
8955 浏览 16 评论
4036 浏览 18 评论
1078浏览 3评论
558浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
551浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2286浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1848浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-10 14:56 , Processed in 0.811000 second(s), Total 49, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号