这两天项目有点多,其中一个是在ucos上实现数据采集功能,以前看过ucos的代码,但是断断续续的,也没有记录,现在基本忘的差不多了,所以把看代码后的一些思考和经验总结出来,供自己以后阅读。因为是循序渐进的,有很多不足,以后为修正,也希望网络上的大牛能指出不足。
看了ucos部分内核代码后,感觉相对于linux这些航母级的操作系统来说,ucos整个内核还是比较轻量级的。并且没有有复杂的文件系统和内存管理后,整个内核看起来就更加爽。但是“麻雀虽小五脏俱全”(这句话老听别人说,咱也用用),实时操作系统该有的ucos都有。
关于内核调度这块,不会记录与平台相关的部分,也就是任务切换时arm寄存器保存和恢复的过程,这部分只知道大致的原理,没有细研究过,因为暂时不用,所以也懒得去查arm芯片手册。
ucos的任务调度:
上课时,老师在说ucos时讲,ucos唯一值得说的就是它的任务调度算法,现在回想起来还是有道理的,我看过一些实时内核的代码,关于调度算法不一样外,其它的像什么event, messge, mailbox, 还有mutex之类的实现机制,都非常相似,跟我们打大学学的操作系统原理上讲的查不多。所以就记录下ucos任务调度算法:
整个算法比较重要的就是四个数组:
unsigend char OSRdyGrp; 辅助表,它长8bits,每个比特指示OSRdyTbl一行的状体,eg: bit0->OSRdyTbl[0]; bit1->OSRdyTbl[1]......bit7->OSRdyTbl[7].当后者任何一位为一时,前者bit位为1.
unsigend char OSRdyTbl[8]; 8*8 = 64 按优先级排列task,每个bit代表一个task, bit位唯一表示相应的task, ready to run;
unsigned char OSMapTbl[8]; 辅助表 算法中用到,当任务进入/脱离就绪表时都会用到它。
unsigned char OSUnMapTbl[256]; 辅助表 当需要在任务就绪表中找出最高优先级的任务时,就需要用到这个表,这个表比较怪异,它的成员记录的是二进制数据末尾为1的位置。
详细的算法可以查看代码OS_SchedNew(), OS_Sched();
发生任务调度的地方:
当初始化时,会建立两个任务,一个是idle task,还有一个是stat task,这两个任务是ucos默认建立的,stat task是用于统计cpu的使用情况, idle task是优先级最低的task,它存在的意义是,当所有任务都没事做时,就跳到它里面,它只是让一个变量自加,这个变量给stat task用。处此之外就可以建立自己的task。
在建立task的时候,有OSTaskCreate()函数,这个函数在建立后会有条件的选择是否立即调度,这个条件是OSRunning == 1, 但是在初始化的时候这个值给的是0,在所有任务建立后才在OSStart()函数里面赋值为1。也就是在OSStart之后才会允许调度。
那些地方会执行任务调度:
每个task都会运行一段时间后执行任务调度,将执行权转移(idle task例外,下面有讲)到其它的task,目前看到的转移方式是在task里面执行OSTimeDly()函数,这个函数会将要delay的tick数,放入到当前task tcb的OSTCBDly变量里面,然后就会执行OS_Sched()执行调度。
疑问,当系统运行到idle task之后,怎么从idle task里面跳出来,因为idle task没有执行任务调度的代码,--------中断,没有中断打扰他,他是不会让出执行权的。在运行idle task的时候中断是开着的,这个时候能够产生调度的中断,目前只看到SysTick_Handler(),这个中断,也许还有其它的中断发生后会有可能产生调度。
SysTick_Handler()是system tick发生后运行的ISR,(system tick 类似于ucos的脉搏,一般设置为1秒100个ticks, 也就是每个tick占10ms, 每10ms都会导致system tick中断),这个中断函数做了两件事:1. 中断发生后会遍历当前所有task的tcb->OSTCBDly变量,并逐个自减1,自减的同时判断OSTCBDly是否为零,如果为零就将任务放入就绪表。2.在退出中断函数时,回产生任务调度。 这就解释了为什么在idle task里面会跳出来了。
所以从现在来看,任务调度发生的地方就这些了,后面看到了还会补充。
发生任务调度的地方:
1. 等待mailbox消息,sys_mbox_fetch()
2.等待信号量,sys_sem_wait()
任务切换的过程:
这点开始说了只是知道一个大致的原理,现在就记录下来这个原理,给以后做个参考。
任务切换(单核),主要的思想就是,保存和恢复task在某个时刻的状态,这个状体包括很多东西,与平台有关,以x86和arm比较为例,x86芯片有任务状态寄存器,任务描述符,这些在任务切换的时候都需要保存,而arm里面没有这些东西。但是无非就是那些记录当前芯片运行状态的东西,例如: x86的pc指针,状态寄存器flags, ax,bx,cx,dx,ds,es,ss等,arm的r0-r15(pc),cpsr等等这些寄存器的内容等等,这些在task切换的时候都会保存到对应task的stack里面,同时,新task里面的stack保存的内容会放入对应的芯片寄存器里面,然后产生一个中断或exception之类的动作,芯片就会在新task上接着运行。反之同样的道理。-------------这是自己的理解。