前语
想了好久,要不要写这篇文章?最终觉得对操作体系感兴趣的人仍是许多,写吧.我纷歧定能造出玉,但我能够抛出砖.
包含我在内的许多人都对51运用操作体系呈失望情绪,由于51的片上资源太少.但关于许多要求不高的体系来说,运用操作体系能够使代码变得更直观,易于保护,所以在51上仍有操作体系的生计时机.
盛行的uCos,Tiny51等,其实都不合适在2051这样的片子上用,占资源较多,唯有自已着手,以不变应万变,才能让51也有操作体系可用.这篇贴子的意图,是教会咱们怎么现场写一个OS,而不是给咱们供给一个OS版别.供给的一切代码,也都是示例代码,所以不要由于它没什么功用就说LAJI之类的话.假如把功用写全了,一来估量你也不想看了,二来也失掉灵活性没有价值了.
下面的贴一个示例出来,能够清楚的看到,OS自身只需不到10行源代码,编译后的方针代码60字节,使命切换耗费为20个机器周期.相比之下,KEIL内嵌的TINY51方针代码为800字节,切换耗费100~700周期.仅有不足之处是,每个使命要占用掉十几字节的仓库,所以使命数不能太多,用在128B内存的51里有点难度,但关于52来说问题不大.这套代码在36M主频的STC12C4052上实测,切换使命仅需2uS.
#include
#define MAX_TASKS 2 //使命槽个数.有必要和实践使命数一至
#define MAX_TASK_DEP 12 //最大栈深.最低不得少于2个,保存值为12.
unsigned char idata 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)的使命槽中.假如该槽华夏来就有使命,则原使命丢掉,但体系自身不会产生过错.
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;}
/*======================以下为测验代码======================*/
void task1()
{
static unsigned char i;
while(1){
i++;
task_switch(); //编译后在这儿打上断点
}
}
void task2()
{
static unsigned char j;
while(1){
j+=2;
task_switch(); //编译后在这儿打上断点
}
}
void main()
{
//这儿装载了两个使命,因此在界说MAX_TASKS时也有必要界说为2
task_load(task1, 0); //将task1函数装入0号槽
task_load(task2, 1); //将task2函数装入1号槽
os_start(0);
}
这样一个简略的多使命体系尽管不能称得上真实的操作体系,但只需你了解了它的原理,就能轻易地将它扩展得十分强壮,想知道要怎么做吗?
一.什么是操作体系?
人脑比较容易承受"类比"这种表达方式,我就用"公交体系"来类比"操作体系"吧.
当咱们要处理一个问题的时分,是用某种处理手法去完结它,这便是咱们常说的"方法",计算机里叫"程序"(有时分也能够叫它"算法").
以出行为例,当咱们要从A地走到B地的时分,能够走着去,也能够飞着去,能够走直线,也能够绕弯路,只需能从A地到B地,都叫作方法.这种从A地到B的需求,相当于计算机里的"使命",而完成从A地到B地的方法,叫作"使命处理流程"
很显然,这些走法中,并不是每种都合理,有些傻子都会选用的,有些是傻子都不采会用的.用计算机的话来说便是,有的使命处理流程好,有的使命处理流程好,有的处理流程差.
能够概括出这么几种真实算得上方法的方法:
有些走法比较快速,合适于赶时刻的人;有些走法比较省劲,合适于懒人;有些走法比较廉价,合适于贫民.
用计算机的话说便是,有些省CPU,有些流程简略,有些对体系资源要求低.
现在咱们能够看到一个问题:
假如全世界一切的资源给你一个人用(单使命独占悉数资源),那最合适你需求的方法便是好方法.但事实上要外出的人许多,例如10个人(10个使命),却只需1辆车(1套资源),这叫作"资源争用".
假如每个人都要运用最合适他需求的方法,那司机就只好给他们一人跑一趟了,而在任一时刻里,车上只需一个乘客.这叫作"次序履行",咱们能够看到这种方法对体系资源的糟蹋是严峻的.
假如咱们没有法力将1台车变成10台车来送这10个人,就只好拟定一些机制和约好,让1台车看起来像10台车,来处理这个问题的方法想必咱们都知道,那便是拟定公交线路.
最简略的方法是将一切旅客需要走的起点与结尾串成一条线,车在这条线上开,乘客则自已决议上下车.这便是最简略的公交线路.它很差劲,但最少处理客人们对车争用.对应到计算机里,便是把一切使命的代码混在一同履行.
这样做既不优异雅,也没功率,所以司机想了个方法,把这些客户叫到一同商议,将一切客人出行的起点与结尾罗列出来,计算这些线路的运用频度,然后拟定出公交线路:有些道路能够兼并起来成为一条线路,而那些不能兼并的道路,则另行拓荒行车车次,这叫作"使命界说".其他,关于人多道路,车次排多点,时刻上也优先组织,这叫作"使命优先级".
通过这样的组织后,尽管仍只需一辆车,但运载才能却大多了.这套车次/道路的按排,便是一套"公交体系".哈,知道什么叫操作体系了吧?它也便是这么样的一种约好.
操作体系:
咱们先回过头概括一下:
轿车 体系资源.首要指的是CPU,当然还有其它,比方内存,定时器,中止源等.
客户出行 使命
正在走的道路 进程
一个一个的运送旅客 次序履行
一起运送一切旅客 多使命并行
按不同的运用频度拟定道路并优先跑较繁忙的道路 使命优先级
计算机内有各种资源,单从硬件上说,就有CPU,内存,定时器,中止源,I/O端口等.并且还会派生出来许多软件资源,例如音讯池.
操作体系的存在,便是为了让这些资源能被合理地分配.
最终咱们来总结一下,所谓操作体系,以咱们现在权宜的了解便是:为"处理计算机资源争用而拟定出的一种约好".
二.51上的操作体系
关于一个操作体系来说,最重要的莫过于并行多使命.在这儿要弄清一下,不要拿当年的DOS来说事,年代不同了.何况当年IBM和小比尔着急将PC搬上市,所以才抄袭PLM(好象是叫这个名吧?记不太清)搞了个今日看来很"偷工减料"的DOS出来.看看其时真实的操作体系—UNIX,它还在纸上时就已经是多使命的了.
关于咱们PC来说,要完成多使命并不是什么问题,但换到MCU却很头痛:
1.体系资源少
在PC上,CPU主频以G为单位,内存以GB为单位,而MCU的主频一般只需十几M,内存则是Byts.在这么少的资源上一起运转多个使命,就意味着操作体系有必要尽可能的少占用硬件资源.
2.使命实时性要求高
PC并不需要太关怀实时性,由于PC上简直一切的实时使命都被专门的硬件所接纳,例如一切的声卡网卡显现上都内置有DSP以及许多的缓存.CPU只需坐在那里指手划脚告知这些板卡怎么敷衍实时信息就行了.
而MCU不同,实时信息是靠CPU来处理的,缓存也十分有限,乃至没有缓存.一旦信息抵达,CPU有必要在极短的时刻内呼应,不然信息就会丢掉.
就拿串口通讯来举例,在规范的PC架构里,巨大的内存答应将信息保存满足长的时刻.而关于MCU来说内存有限,例如51仅有128字节内存,还要扣除去寄存器组占用掉的8~32个字节,所以一般都仅用几个字节来缓冲.当然,你能够将数据的接纳与处理的进程兼并,但关于一个操作体系来说,不引荐这么做.
假定以115200bps通讯速率向MCU传数据,则每个字节的传送时刻约为9uS,假定缓存为8字节,则串口处理使命有必要在70uS内呼应.
这两个问题都指向了同一种处理思路:操作体系有必要轻量轻量再轻量,最好是不占资源(那当然是做梦啦).
可用于MCU的操作体系许多,但合适51(这儿的51专指无扩展内存的51)简直没有.前阵子见过一个"圈圈操作体系",那是我所见过的操作体系里最轻量的,但仍有改善的地步.
许多人以为,51底子不合适运用操作体系.其实我对这种说法并不彻底承受,不然也没有这篇文章了.
我的观点是,51不合适选用"通用操作体系".所谓通用操作体系便是,不论你是什么样的使用需求,也不论你用什么芯片,只需你是51,通通用同一个操作体系.
这种主意关于PC来说没问题,关于嵌入式来说也不错,对AVR来说还将就,而关于51这种"赤贫型"的MCU来说,不可.
怎样行?因地制宜,现场依据需求构建一个操作体系出来!
看到这儿,估量许多人要翻白眼了,大体上两种:
1.操作体系那么杂乱,说造就造,当自已是神了?
2.操作体系那么杂乱,现场造一个会不会出BUG?
哈哈,看清楚了?问题出在"杂乱"上面,假如操作体系不杂乱,问题不就处理了?
事实上,许多人对操作体系的了解是片面的,操作体系纷歧定要做得很杂乱很全面,就算仅个多使命并行管理才能,你也能够称它操作体系.
只需你对多使命并行的原理有所了解,就不难现场写一个出来,而一旦你做到了这一点,为各使命间组织通讯约好,使之开展成一个为你的使用体系量身定做的操作体系也就不难了.
为了加深对操作体系的了解,能够看一看<<演化>>这份PPT,让你充沛了解一个并行多使命是怎么一步步从次序流程演化过来的.里边还提到了许多人都在用的"状态机",你会发现操作体系跟状态机从原理上其实是多么类似.会用状态机写程序,都能写出操作体系.
三.我的第一个操作体系
直接进入主题,先贴一个操作体系的演示出来.咱们能够看到,本来操作体系能够做得么简略.
当然,这儿要声明一下,这玩意儿其实算不上真实的操作体系,它除了并行多使命并行外底子没有其他功用.但凡事都从简略开端,搞懂了它,就能依据使用需求,将它扩展成一个真实的操作体系.
好了,代码来了.
将下面的代码直接放到KEIL里编译,在每个task?()函数的"task_switch();"那里打上断点,就能够看到它们的确是"一起"在履行的.
#include
#define MAX_TASKS 2 //使命槽个数.有必要和实践使命数一至
#define MAX_TASK_DEP 12 //最大栈深.最低不得少于2个,保存值为12.
unsigned char idata task_stack[MAX_TASKS][MAX_TASK_DEP];//使命仓库.
unsigned char task_id; //其时活动使命号