|
说执行效率近乎裸机,可能一听这话就有很多人想都不想,看都不看,直接就要拍砖。
编写此内核的起因和探索过程我几天前已经发到坛子上了http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4310923&bbs_page_no=1&bbs_id=1000
整个内核没用利用任何中断,程序执行过程无任何打断,与裸机代码没任何区别,所以,如题绝不夸张。
没有利用定时中断作为任务调度的时钟基准,这个我不敢说后无来者,但应该是前无古人。
几天前写完,经过一定测试,现在发出跟大家分享一下,希望能得到大家的指点。
先给大家介绍下这个Z1/OS内核的亮点功能:
1、任务动态建立、动态删除:比如我们要做一个路由器,对每一台连接的PC都可以是相同的程序,当有PC机链接时,单独建立一个任务处理,当PC机关机,这边还可以动态删除任务,内存利用率极高,对编程也提供了最简便的方法。
2、无定时中断打断:这在各种OS上,绝对是个难题,OS的性能损失主要也是源于中断的时间,而Z1/OS,没利用定时中断,就更不要说损失了。
3、节拍小:每个OS上都会有时钟最小节拍,如果设置大了,响应能力慢,设置小了,性能损失严重。而本内核基于非中断模式,可以把节拍放到非常小,也无需担心性能损失问题。
4、体积小、可裁减:内核功能全开,也仅需要2K左右ROM,74B左右RAM,而且还可根据需要裁剪,最小可致1K左右ROM, 45B左右RAM。
5、最大支持8个优先级,256个以上任务:支持8个优先级,同优先级任务轮渡。
6、可移植性高:不超过80行的内敛汇编,在任何单片机及环境下都极易实现。
7、可调式性:内核内部采用很多容错检测来提高内核安全性,但这样往往造成无用代码过多,效率低下。而Z1/OS,可分两版本编译,
当OS_DEBUG_EN为1时,内核编译为调试版本,进行很多错误检测。当开发完成时,可设置OS_DEBUG_EN为0。即安全又高效。
8、易学习:各种函数、宏、移植文件布局,都采用UCOS、SMAllOS 51等内核通用命名方式,让有OS基础的人,第一时间上手。
先上一个例子,让大家看看其易用性,其效果是让MEGA16、8MHZ上,PA、PB、PC、PD四组口连接四组灯,让其以不同频率、不同图案变换:
#include"Z1OS.h"
int main(void)
{
OSInit();
OSTaskCreate(Task0, 70, 77);
OSTaskCreate(Task1, 70, 78);
OSTaskCreate(Task2, 70, 79);
OSTaskCreate(Task3, 70, 80);
OSStart();
}
其中
OSInit();是系统初始化
OSTaskCreate()第一个参数是指定任务函数名、第二个是任务堆栈大小、第三个是任务ID
Task0、Task1、Task2、Task3是四组图案的函数,每一本身都是一个大循环,就是普通小灯程序代码,不贴了。
OSStart();系统运行。
用过OS的人看着眼熟吧,没用过的也可望名之意。但其中没有让用户自己指定堆栈地址,而是仅仅指定个堆栈大小,是因为此内核是建立在动态任务的基础上的,无需自己静态建立堆栈那么烦琐。
给大家介绍下,Z1/OS目前提供的服务函数介绍吧:
/*
*********************************************************************************************************
* 函数原型:BOOLEAN OSTaskCreate(void(*Task)(void),
* INT16U StkSize,
* INT8U TaskId);
* 函数介绍
* 输入参数:void (*Task)(void)
* 为指向新任务执行函数地址的指针
* INT16U StkSize
* 为新任务分配的堆栈大小
* INT8U TaskId
* 新任务ID
* 输出参数:无
* 返回值 :TRUE 任务建立成功
* FALSE 任务建立失败(内存不足)
*********************************************************************************************************
*/
当OS_TASK_SUSPEND_RESUME_EN设置为1时可使用
/*
*********************************************************************************************************
* 函数原型:void OSTaskSuspend(INT8U TaskID);
* 函数介绍:将指定ID任务挂起(停止)
* 输入参数:INT8U TaskID
* 指定任务ID值
* 输出参数:无
* 返回值 :无
*********************************************************************************************************
*/
当OS_TASK_SUSPEND_RESUME_EN设置为1时可使用
/*
*********************************************************************************************************
* 函数原型:void OSTaskResume(INT8U TaskID);
* 函数介绍:将指定ID的任务就绪,
* 即将被暂停或挂起(停止)的任务恢复
* 输入参数:INT8U TaskID
* 指定任务ID值
* 输出参数:无
* 返回值 :无
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 函数原型:void OSTimeDly(INT8U Ticks);
* 函数介绍:将当前任务暂停若干节拍
* 输入参数:INT8U Ticks
要暂停的节拍数
* 输出参数:无
* 返回值 :无
*********************************************************************************************************
*/
当OS_TIME_DLY_MS_EN为1可使用
特殊说明,此函数采用跟GCC自带_delay_ms一样的方式,借用编译器内部方式实现,虽然参数为FP64,其只是会提高精度,而不会真的浪费内存按此编译。
/*
*********************************************************************************************************
* 函数原型:void OSTimeDlyMS(FP64 Ms);
* 函数介绍:将当前任务暂停指定的毫秒数
* 输入参数:FP64 Ms
* 要暂停的毫秒数
* 输出参数:无
* 返回值 :无
*********************************************************************************************************
*/
当OS_TIME_DLY_HMS_EN为1和OS_TIME_GET_SET_EN为1时,可使用
/*
*********************************************************************************************************
* 函数原型:void OSTimeDlyHMS(INT8U Hours, INT8U Minutes, INT8U Seconds);
* 函数介绍:将当前任务暂停指定的一段时间
* 输入参数:INT8U Hours
要暂停的小时数
INT8U Minutes
要暂停的分钟数
INT8U Seconds
要暂停的秒数
* 输出参数:无
* 返回值 :无
*********************************************************************************************************
*/
宏OSMalloc(size) OSFree(*p)实现内存动态申请释放,由于GCC性能卓越,暂时这两函数仅调用GCC自带的malloc、free,
以后我会实现一个轻量级的内存池,来防止内存碎片。
宏OS_ASSERT可以用来检测程序编写时的逻辑错误,本内核本身大量采用了这个宏来进行错误检测。当调试完毕,把OS_DEBUG_EN为0,自然将这些检测取消。应用程序也可以调用此宏来进行调试。
下午还有课,写不完了,把内核传上来,大家可以试一试。
此时基于GCC_AVR、MEGA16、8MHZ的版本,里面有个程序,就是上面说的四组灯,还有Proteus的仿真,大家可以先试试。
上面提到OS_XX_EN都是在os_cfg.h中配置,配置完运行自带的那个MakeFile即可。
上完课我在详说,不明白的也可以发到我邮箱,欢迎大家使用。
——————————————————————————————————————————————————————————————
好的,继续。有网友让说说这个内核的时钟基准和任务调度算法,那就先说说。这个其实就是这内核最重要的部分,要讲清楚,就说说过程。
以往协作式内核和剥夺式内核的时钟基准全部采用定时中断,这性能损失对于高端机不算什么,但对于32MHZ以下的单片机,这些损失是巨大。
拿uCOSII来说,我将它非常小心的精简、改造后移植到了Mega16,但并没有什么使用价值。在Mega128上,将4K RAM全部用掉都无法将uCOSII全部功能展开。
而且uCOSII的建议系统频率是多少?是10HZ - 100HZ!而为了适应我们低性能单片机,我勉强将其调到400HZ,但这是性能损失已经无法估量,其每2ms多点就会被中断打断一回,这太可怕了。
而我们需要的延时最少也要ms级,那是1000HZ!换句话说uCOSII移植到AVR仅仅是为了移植而移植~~并没有太大使用价值(声明下,我并没有攻击uCOSII,而且恰恰相反,我是uCOSII的忠实爱好者,在这里只是就是论事)
而这些类系统为什么需要那么大体积、而且节拍无法太小?并不其作者不愿意小,而是因为那是剥夺式内核,要考虑资源互斥等问题,所以体积再所难免。
剥夺式内核在高端非强实时机上,非常好用。但在8位机上,并不是很合适。
所以我就定了两个目标来写这个Z1/OS内核,就是一是节拍可以无限小,来把性能提高到裸机状态;二是基于协作式内核来弃用互斥信号等机制,这样可以减小体积。
我采用的时钟基准是定时器,但不中断!就是以大分频(防溢出)定时器作为计时,在需要调度时读取定时器作为基准,然后清零!这样每个任务执行时不会受任何影响,仅在主动切换时计算一回。以内8MHz的Mega16来说,理论上可以把系统频率提高到1MHz,当然这仅仅是理论。我在后面给大家下载的文件中调的是2000Hz,也就是最小节拍是0.5毫秒,已经足够用了。经计算及仿真试验,提高到0.1毫秒不成问题。
过于精细的时间,因为是协作式、无资源共享问题,可以跟裸机时一样,放入中断。这样看来,我这第一个目标完成的十分完美,可以说在8位机上已经到了极致。
对于第二个目标,一个不需要邮箱、信号量的内核,本身体积就非常小。而我在调度算法上,为了实现无任务数限制、让用户使用方便、任务动态删除建立,所以采用的是任务链的形式,将很多在8位机不可思议的事情实现了。但这面临一个新问题,怎么保证链表搜索效率?这个首先要先解决动态内存分配问题,我原计划自己实现一个轻量级内存动态管理及内存池。但后来发现,GCC中的自带的内存分配效率已经非常高,所以直接采用了编译内置的函数,以后只要实现个小型内存池就可以减少内存碎片的产生了。而后,我单独为每个任务链定义了一种畸形链表结构,说是畸形,是因为它具有记忆能力,但却只是普通的单链,整个搜索代码不超50行。
明白这些思路,再看源码,就会清晰很多。这是一段插曲,言归正传。
——————————————————————————————————————————————————————————————
我准备整理的全面一点的例子一起发出,让大家很容易使用,稍等一天。Thanks
版权声明:但凡回帖者,即获得学习及个人开发授权
如用作商用产品开发,请联系作者并经同意后使用。邮箱: yrloy@live.cn
完全编译才如此轻量 (原文件名:全功能尺寸.JPG)
例子程序的仿真 (原文件名:例子程序的仿真.JPG)
带一个例子程序,带内核源码,有中文注释ourdev_590081CG5NX4.zip(文件大小:98K) (原文件名:Z1OS.zip) |
阿莫论坛20周年了!感谢大家的支持与爱护!!
你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
|