




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、51单片机多任务操作系统的原理与实现51单片机多任务操作系统的原理与实现-一个超轻量级的操作系统 、户 、.刖百想了很久,要不要写这篇文章最后觉得对操作系统感兴趣的人还是很多,写吧.我不一定能造出玉,但我可以抛出砖.包括我在内的很多人都对51使用操作系统呈悲观态度,因为51的片上资源太少. 但对于很多要求不高的系统来说,使用操作系统可以使代码变得更直观,易于维 护,所以在51上仍有操作系统的生存机会.流行的uCos,Tiny51等,其实都不适合在2051这样的片子上用,占资源较多,唯有 自已动手,以不变应万变,才能让51也有操作系统可用.这篇贴子的目的,是教会 大家如何现场写一个OS,而不是给
2、大家提供一个 。或本.提供的所有代码,也都 是示例代码,所以不要因为它没什么功能就说 LAJI之类的话.如果把功能写全 了,一来估计你也不想看了,二来也失去灵活性没有价值了 .下面的贴一个示例出来,可以清楚的看到,OS本身只有不到10行源代码,编译后 的目标代码60字节,任务切换消耗为20个机器周期.相比之下,KEIL内嵌的 TINY51目标代码为800字节,切换消耗100700周期.唯一不足之处是,每个任务 要占用掉十几字节的堆栈,所以任务数不能太多,用在128B内存的51里有点难 度,但对于52来说问题不大.这套代码在36M主频的STC12C4052k实测,切换任 务仅需2uS.#incl
3、ude <>?#define MAX_TASKS 2? ?微和实际任务数一至?#define MAX_TASK_DEP 12? ?氐不得少于 2 个,保守值为 12.?unsigned char idata task_stackMAX_TASKSMAX_TASK_DEP;?unsigned char task_id;? ?指定的函数(参数1)装入指定(参数2)的任务槽中.如果该槽中麻就有任务,则原任务丢失,但系统本身不会发生错误.?void task_load(unsigned int fn, unsigned char tid) ? task_sptid = task_stack
4、tid + 1;? task_stacktid0 = (unsigned int)fn & 0xff;? task_stacktid1 = (unsigned int)fn >> 8;?.用该宏后,将永不返回.?#define os_start(tid) task_id = tid,SP = task_sptid;return;?/*=二=下为测试代房=*/?void task1()? static unsigned char i;? while(1)? i+;? task_switch();? 么是操作系统人脑比较容易接受 "类比 "这种表达方式 ,
5、我就用 "公交系统 "来类比"操作系统"吧.当我们要解决一个问题的时候, 是用某种处理手段去完成它 , 这就是我们常说的 "方法 ", 计算机里叫 "程序 "( 有时候也可以叫它 "算法 ").以出行为例,当我们要从A地走到B地的时候,可以走着去,也可以飞着去,可以走 直线,也可以绕弯路,只要能从A地到B地,都叫作方法.这种从A地到B的需求, 相当于计算机里的"任务”,而实现从A地到B地的方法,叫作"任务处理流程" 很显然 , 这些走法中 , 并不是每种都合理, 有
6、些傻子都会采用的 , 有些是傻子都不采会用的 . 用计算机的话来说就是, 有的任务处理流程好, 有的任务处理流程好,有的处理流程差 .可以归纳出这么几种真正算得上方法的方法:有些走法比较快速, 适合于赶时间的人; 有些走法比较省事, 适合于懒人; 有些走法比较便宜 , 适合于穷人.用计算机的话说就是,有些省CPU有些流程简单,有些对系统资源要求低.现在我们可以看到一个问题 :如果全世界所有的资源给你一个人用 ( 单任务独占全部资源), 那最适合你需求的方法就是好方法. 但事实上要外出的人很多 , 例如 10 个人 (10 个任务 ), 却只有 1辆车 (1 套资源 ), 这叫作 "资
7、源争用 ".如果每个人都要使用最适合他需求的方法, 那司机就只好给他们一人跑一趟了 ,而在任一时刻里, 车上只有一个乘客. 这叫作 " 顺序执行 ", 我们可以看到这种方法对系统资源的浪费是严重的 .如果我们没有法力将1 台车变成 10 台车来送这10 个人 , 就只好制定一些机制和约定 , 让 1 台车看起来像10 台车 , 来解决这个问题的办法想必大家都知道, 那就是制定公交线路.最简单的办法是将所有旅客需要走的起点与终点串成一条线, 车在这条线上开,乘客则自已决定上下车. 这就是最简单的公交线路. 它很差劲 , 但起码解决客人们对车争用 . 对应到计算机里,
8、 就是把所有任务的代码混在一起执行.这样做既不优异雅, 也没效率 , 于是司机想了个办法, 把这些客户叫到一起商量,将所有客人出行的起点与终点罗列出来, 统计这些线路的使用频度, 然后制定出公交线路 : 有些路线可以合并起来成为一条线路, 而那些不能合并的路线 , 则另行开辟行车车次, 这叫作 "任务定义 ". 另外 , 对于人多路线, 车次排多点 , 时间上也优先安排 , 这叫作 " 任务优先级".经过这样的安排后 , 虽然仍只有一辆车, 但运载能力却大多了 . 这套车次 / 路线的按排 , 就是一套 " 公交系统 ". 哈 , 知
9、道什么叫操作系统了吧它也就是这么样的一种约定 .操作系统 :我们先回过头归纳一下 :汽车???系统资源.主要指的是CPU当然还有其它,比如内 存 , 定时器 , 中断源等 .客户出行?任务正在走的路线?进程一个一个的运送旅客?顺序执行同时运送所有旅客?多任务并行按不同的使用频度制定路线并优先跑较繁忙的路线? 任务优先级计算机内有各种资源,单从硬件上说,就有CPU内存,定时器,中断源,I/O端口等 . 而且还会派生出来很多软件资源, 例如消息池.操作系统的存在, 就是为了让这些资源能被合理地分配.最后我们来总结一下 , 所谓操作系统, 以我们目前权宜的理解就是: 为"解决计算机资源争用
10、而制定出的一种约定".二 .51 上的操作系统对于一个操作系统来说, 最重要的莫过于并行多任务. 在这里要澄清一下 , 不要拿当年的DO既说事,时代不同了 .况且当年 旧M和小比尔着急将PO上市,所以才 抄袭PLM好象是叫这个名吧记不太清)搞了个今天看来很"粗制滥造”的DOSB 来 . 看看当时真正的操作系统-UNIX, 它还在纸上时就已经是多任务的了 .对于我们PC来说,要实现多任务并不是什么问题,但换到MCI#很头痛:1. 系统资源少在PC上,CPU主频以G为单位,内存以GB为单位,而MCU勺主频通常只有十几 M, 内存则是 Byts. 在这么少的资源上同时运行多个任务
11、, 就意味着操作系统必须尽可能的少占用硬件资源.2. 任务实时性要求高PC并不需要太关心实时性,因为PC上几乎所有的实时任务都被专门的硬件所接 管,例如所有的声卡网卡显示上都内置有 DSP以及大量的缓存.CPU只需坐在那里 指手划脚告诉这些板卡如何应付实时信息就行了 .而MCUP同,实时信息是靠CPI#处理的,缓存也非常有限,甚至没有缓存.一旦信 息到达,CPU必须在极短的时间内响应,否则信息就会丢失.就拿申口通信来举例,在标准的PC架构里,巨大的内存允许将信息保存足够长的 时间.而对于MC廉说内存有限,例如51仅有128字节内存,还要扣除掉寄存器组 占用掉的 832 个字节 , 所以通常都仅
12、用几个字节来缓冲 . 当然 , 你可以将数据的 接收与处理的过程合并, 但对于一个操作系统来说 , 不推荐这么做.假定以115200bps通信速率向MCI#数据,则每个字节的传送时间约为9uS,假定缓存为 8 字节 , 则串口处理任务必须在70uS 内响应 .这两个问题都指向了同一种解决思路: 操作系统必须轻量轻量再轻量, 最好是不占资源 (那当然是做梦啦 ).可用于MCU勺操作系统很多,但适合51(这里的51专指无扩展内存的51)几乎没 有 . 前阵子见过一个" 圈圈操作系统", 那是我所见过的操作系统里最轻量的 , 但仍有改进的余地.很多人认为,51 根本不适合使用操作
13、系统. 其实我对这种说法并不完全接受 , 否则也没有这篇文章了 .我的看法是,51 不适合采用 "通用操作系统". 所谓通用操作系统就是, 不论你是什么样的应用需求, 也不管你用什么芯片 , 只要你是 51, 通通用同一个操作系统.这种想法对于PC来说没问题,对于嵌入式来说也不错,对AV陈说还凑合,而对于 51这种"贫穷型"的MCI#说,不行.怎样行量体裁衣, 现场根据需求构建一个操作系统出来!看到这里 , 估计很多人要翻白眼了 , 大体上两种 :1. 操作系统那么复杂, 说造就造 , 当自已是神了2. 操作系统那么复杂, 现场造一个会不会出 BUG哈哈
14、 , 看清楚了问题出在" 复杂 " 上面 , 如果操作系统不复杂, 问题不就解决了事实上 , 很多人对操作系统的理解是片面的 , 操作系统不一定要做得很复杂很全面 , 就算仅个多任务并行管理能力 , 你也可以称它操作系统.只要你对多任务并行的原理有所了解, 就不难现场写一个出来, 而一旦你做到了这一点 , 为各任务间安排通信约定, 使之发展成一个为你的应用系统量身定做的操作系统也就不难了 .为了加深对操作系统的理解,可以看一看 <<演变>>这份PPT,让你充分了解一个并行多任务是如何一步步从顺序流程演变过来的 . 里面还提到了很多人都在用的"
15、; 状态机 ", 你会发现操作系统跟状态机从原理上其实是多么相似. 会用状态机写程序 , 都能写出操作系统.三 . 我的第一个操作系统直接进入主题 , 先贴一个操作系统的示范出来. 大家可以看到 , 原来操作系统可以做得么简单.当然 , 这里要申明一下, 这玩意儿其实算不上真正的操作系统, 它除了并行多任务并行外根本没有别的功能. 但凡事都从简单开始 , 搞懂了它 , 就能根据应用需求,将它扩展成一个真正的操作系统.好了 , 代码来了 .将下面的代码直接放到 KEIL 里编译 , 在每个 task() 函数的 "task_switch();" 那里打上断点 , 就
16、可以看到它们的确是" 同时 "在执行的 .#include <>#define MAX_TASKS 2? ?须和实际任务数一至#define MAX_TASK_DEP 12? ?低不得少于2 个, 保守值为 12.unsigned char idata task_stackMAX_TASKSMAX_TASK_DEP;unsigned char task_id;? ?指定的函数(参数 1) 装入指定 (参数 2) 的任务槽中 . 如果该槽中原来就有任务, 则原任务丢失, 但系统本身不会发生错误.void task_load(unsigned int fn, uns
17、igned char tid)? task_sptid = task_stacktid + 1;? task_stacktid0 = (unsigned int)fn & 0xff;? task_stacktid1 = (unsigned int)fn >> 8;用该宏后 , 将永不返回 .#define os_start(tid) task_id = tid,SP = task_sptid;return;/*=以下为测试代码=*/void task1()? static unsigned char i;? while(1)? i+;? task_switch();现在来看
18、看这个多任务系统的原理:这个多任务系统准确来说, 叫作 " 协同式多任务".所谓 " 协同式 ", 指的是当一个任务持续运行而不释放资源时, 其它任务是没有任何机会和方式获得运行机会, 除非该任务主动释放CPU.在本例里,释放CPUH靠task_switch() 来完成的.task_switch()函数是一个很特殊的函数, 我们可以称它为 " 任务切换器".要清楚任务是如何切换的 , 首先要回顾一下堆栈的相关知识 .有个很简单的问题 , 因为它太简单了 , 所以相信大家都没留意过:我们知道,不论是CALL还是JMP,都是将当前的程序流
19、打断,请问CALL和JMP的 区别是什么你会说:CALL可以RET,JM开行.没错,但原因是啥呢为啥CALLM去的就可以用RET跳回来,JMP过去的就不能用RET来跳回呢很显然,CALL通过某种方法保存了打断前的某些信息,而在返回断点前执行的 RET指令,就是用于取回这些信息.不用多说,大家都知道,"某些信息”就是PC指针,而"某种方法"就是压栈.很幸运 , 在 51 里 , 堆栈及堆栈指针都是可被任意修改的, 只要你不怕死. 那么假如在执行RETt将堆栈修改一下会如何往下看:当程序执行CALL后,在子程序里将堆栈刚才压入的断点地址清除掉,并将一个函 数的地址压入
20、,那么执行完RET后,程序就跳到这个函数去了 .事实上,只要我们在RET将堆栈改掉,就能将程序跳到任务地方去,而不限于 CALL里压入的地址.重点来了 首先我们得为每个任务单独开一块内存, 这块内存专用于作为对应的任务的堆栈,想将CPU*给哪个任务,只需将栈指针指向谁内存块就行了 .接下来我们构造一个这样的函数:当任务调用该函数时, 将当前的堆栈指针保存一个变量里, 并换上另一个任务的堆栈指针 . 这就是任务调度器了 .OK了,现在我们只要正确的填充好这几个堆栈的原始内容,再调用这个函数,这个任务调度就能运行起来了 .那么这几个堆栈里的原始内容是哪里来的呢这就是" 任务装载 &quo
21、t; 函数要干的事了 .在启动任务调度前将各个任务函数的入口地址放在上面所说的 "任务专用的内存块"里就行了!对了 ,顺便说一下, 这个 "任务专用的内存块" 叫作 "私栈 ", 私栈的意思就是说 , 每个任务的堆栈都是私有的 , 每个任务都有一个自已的堆栈.话都说到这份上了 , 相信大家也明白要怎么做了 :1. 分配若干个内存块, 每个内存块为若干字节:这里所说的 " 若干个内存块" 就是私栈 , 要想同时运行几少个任务就得分配多少块.而"每个子内存块若干字节"就是栈深 . 记住 ,每调一层子
22、程序需要2 字节 . 如果不考虑中断,4 层调用深度, 也就是 8字节栈深应该差不多了 .unsigned char idata task_stackMAX_TASKSMAX_TASK_DEP当然 , 还有件事不能忘, 就是堆指针的保存处. 不然光有堆栈怎么知道应该从哪个地址取数据啊unsigned char idata task_spMAX_TASKS上面两项用于装任务信息的区域, 我们给它个概念叫 "任务槽 ". 有些人叫它 "任务堆", 我觉得还是"槽" 比较直观对了 , 还有任务号. 不然怎么知道当前运行的是哪个任务呢unsi
23、gned char task_id 当前运行存放在1 号槽的任务时, 这个值就是1, 运行 2 号槽的任务时, 这个值就是 22. 构造任务调度函函数:void task_switch()? task_sptask_id = SP;?3. 装载任务 :将各任务的函数地址的低字节和高字节分别入在task_stack 任务号 0 和 task_stack 任务号 1 中 :为了便于使用 , 写一个函数:? task_load( 函数名 , 任务号 )void task_load(unsigned int fn, unsigned char tid) ? task_sptid = task_stac
24、ktid + 1;? task_stacktid0 = (unsigned int)fn & 0xff;? task_stacktid1 = (unsigned int)fn >> 8;4. 启动任务调度器:将栈指针指向任意一个任务的私栈,执行RET指令.注意,这可很有学问的哦,没 玩过堆栈的人脑子有点转不弯:这一 RET,RE倒哪去了嘿嘿,别忘了在RETB已经 将堆栈指针指向一个函数的入口了 .你别把RET*成RET,你把它看成是另一种类 型的JMP就好理解了 .SP = task_sp 任务号 ;return;做完这 4 件事后 ,任务 "并行 "执
25、行就开始了 .你可以象写普通函数一个写任务函数, 只需 ( 目前可以这么说)注意在适当的时候(例如以前调延时的地方)调用一下task_switch(), 以让出CPUJ空制权给别的任务就行了 .最后说下效率问题 .这个多任务系统的开销是每次切换消耗20个机器周期(CALL和RETffi算在内了 ), 贵吗不算贵, 对于很多用状态机方式实现的多任务系统来说, 其实效率还没这么高 - case switch 和 if() 可不像你想像中那么便宜.关于内存的消耗我要说的是, 当然不能否认这种多任务机制的确很占内存 . 但建议大家不要老盯着编译器下面的那行字 "DATA= XXXbyte&q
26、uot;. 那个值没意义, 堆栈没算进去 . 关于比较省内存多任务机制 , 我将来会说到 .概括来说 , 这个多任务系统适用于实时性要求较高而内存需求不大的应用场合,我在运行于36M主频的STC12C4052k实测了一把,切换一个任务不到3微秒.下回我们讲讲用 KEIL 写多任务函数时要注意的事项 .下下回我们讲讲如何增强这个多任务系统, 跑步进入操作系统时代.四. 用 KEIL 写多任务系统的技巧与注意事项C51 编译器很多 ,KEIL 是其中比较流行的一种 . 我列出的所有例子都必须在 KEIL 中使用 . 为何不是因为 KEIL 好所以用它 ( 当然它的确很棒), 而是因为这里面用到了
27、KEIL 的一些特性, 如果换到其它编译器下 , 通过编译的倒不是问题 , 但运行起来可能是堆栈错位, 上下文丢失等各种要命的错误, 因为每种编译器的特性并不相同 . 所以在这里先说清楚这一点 .但是 , 我开头已经说了 , 这套帖子的主要目的是阐述原理, 只要你能把这几个例子消化掉 , 那么也能够自已动手写出适合其它编译器的 OS.好了 , 说说 KEIL 的特性吧 , 先看下面的函数:sbit sigl = P1A7;void func1()? register char data i;? i = 5;? do?sigl = !sigl;? ?while(-i);你会说 , 这个函数没什么
28、特别的嘛! 呵呵 , 别着急 , 你将它编译了 , 然后展开汇编代码再看看 :? 193: void func1()? 194:? register char data i;? 195:? i = 5;?C:0x00C3? 7F05? MOV? R7,#0x05? 196:? do? 197:? sigl = !sigl;?C:0x00C5? B297? CPL? sigl? 198:? while(-i);?C:0x00C7? DFFC? DJNZ? R7,C:00C5? 199: ?C:0x00C9? 22? RET?看清楚了没这个函数里用到了R7,却没有对R7进行保护!有人会跳起来了 :
29、 这有什么值得奇怪的 , 因为上层函数里没用到 R7 啊. 呵呵 , 你说的没错 , 但只说对了一半: 事实上 ,KEIL 编译器里作了约定, 在调子函数前会尽可能释放掉所有寄存器. 通常性况下 , 除了中断函数外, 其它函数里都可以任意修改所有寄存器而无需先压栈保护 ( 其实并不是这样, 但现在暂时这样认为 , 饭要一口一口吃嘛 , 我很快会说到的 ).这个特性有什么用呢有! 当我们调用任务切换函数时, 要保护的对象里可以把所有的寄存器排除掉了 , 就是说 , 只需要保护堆栈即可!现在我们回过头来看看之前例子里的任务切换函数:void task_switch()? task_sptask_i
30、d = SP;?看到没 , 一个寄存器也没保护, 展开汇编看看, 的确没保护寄存器.好了 , 现在要给大家泼冷水了 , 看下面两个函数:void func1()? register char data i;? i = 5;? do? sigl = !sigl;? ?while(-i);void func2() ? register char data i;? i = 5;? do? func1();? ?while(-i); 父函数 fun2() 里调用 func1(), 展开汇编代码看看:? 193: void func1()? 194:? register char data i;? 19
31、5:? i = 5;?C:0x00C3? 7F05? MOV? R7,#0x05? 196:? do? 197:? sigl = !sigl;?C:0x00C5? B297? CPL? sigl? 198:? while(-i);?C:0x00C7? DFFC? DJNZ? R7,C:00C5? 199: ?C:0x00C9? 22? RET? 200: void func2()? 201:? register char data i;? 202:? i = 5;?C:0x00CA? 7E05? MOV? R6,#0x05? 203:? do? 204:? func1();?C:0x00CC?
32、 11C3? ACALL? func1(C:00C3)? 205:? while(-i);C:0x00CE? DEFC? DJNZ? R6,C:00CC? 206: ?C:0x00D0? 22? RET?看清楚没函数func2()里的变量使用了寄存器 R6,而在funci和func2里都没保 护.听到这里,你可能又要跳一跳了 :func1()里并没有用到R6,干嘛要保护没错,但 编译器是怎么知道func1()没用到R6的呢是从调用关系里推测出来的.一点都没错,KEIL 会根据函数间的直接调用关系为各函数分配寄存器, 既不用保护 , 又不会冲突,KEIL 好棒哦 ! 等一下 , 先别高兴 , 换
33、到多任务的环境里再试试:void func1()? register char data i;? i = 5;? do? sigl = !sigl;? ?while(-i);void func2()? register char data i;? i = 5;? do? sigl = !sigl;? ?while(-i);展开汇编代码看看:? 193: void func1()? 194:? register char data i;? 195:? i = 5;?C:0x00C3? 7F05? MOV? R7,#0x05? 196:? do? 197:? sigl = !sigl;C:0x00
34、C5? B297? CPL? sigl? 198:? while(-i);C:0x00C7? DFFC? DJNZ? R7,C:00C5? 199: ?C:0x00C9? 22? RET? 200: void func2()? 201:? register char data i;? 202:? i = 5;?C:0x00CA? 7F05? MOV? R7,#0x05? 203:? do? 204:? sigl = !sigl;C:0x00CC? B297? CPL? sigl? 205:? while(-i);?C:0x00CE? DFFC? DJNZ? R7,C:00CC? 206: C:
35、0x00D0? 22? RET看到了吧哈哈, 这回神仙也算不出来了 . 因为两个函数没有了直接调用的关系 , 所以编译器认为它们之间不会产生冲突 , 结果分配了一对互相冲突的寄存器, 当任务从func1()切换到func2()时,func1()中的寄存器内容就给破坏掉了 .大家可以试着去编译一下下面的程序 :sbit sigl = P1A7;void func1()? register char data i;? i = 5;? do? sigl = !sigl;? task_switch();? ? while (-i);void func2()? register char data i;
36、? i = 5;? do? sigl = !sigl;? task_switch();? ?while(-i);我们这里只是示例 , 所以仍可以通过手工分配不同的寄存器避免寄存器冲突 , 但在真实的应用中 , 由于任务间的切换是非常随机的 , 我们无法预知某个时刻哪个寄存器不会冲突, 所以分配不同寄存器的方法不可取. 那么 , 要怎么办呢这样就行了 :sbit sigl = P1A7;void func1()? static char data i;? while(1)? ?i = 5;? ?do? ?sigl = !sigl;? ?task_switch();while(-i);? ?voi
37、d func2()? static char data i;? while(1)? ?i = 5;? ?do? ?sigl = !sigl;? ?task_switch();? ? while(-i);? ?将两个函数中的变量通通改成静态就行了 . 还可以这么做:sbit sigl = P1A7;void func1()? register char data i;? while(1)? ?i = 5;? ?do? ?sigl = !sigl;? ? while(-i);? ?task_switch();? ?void func2()? register char data i;? while
38、(1)? ?i = 5;? ?do? ?sigl = !sigl;? ? while(-i);? ?task_switch();? ?即,在变量的作用域内不切换任务, 等变量用完了 , 再切换任务. 此时虽然两个任务仍然会互相破坏对方的寄存器内容, 但对方已经不关心寄存器里的内容了 .以上所说的 , 就是 " 变量覆盖 " 的问题 . 现在我们系统地说说关于"变量覆盖 ".变量分两种 , 一种是全局变量, 一种是局部变量( 在这里 , 寄存器变量算到局部变量里 ).对于全局变量, 每个变量都会分配到单独的地址.而对于局部变量,KEIL 会做一个 &quo
39、t;覆盖优化 ", 即没有直接调用关系的函数的变量共用空间 . 由于不是同时使用 , 所以不会冲突, 这对内存小的 51 来说 , 是好事 .但现在我们进入多任务的世界了 , 这就意味着两个没有直接调用关系的函数其实是并列执行的 , 空间不能共用了 . 怎么办呢一种笨办法是关掉覆盖优化功能. 呵呵 , 的确很笨 .比较简单易行一个解决办法是, 不关闭覆盖优化 , 但将那些在作用域内需要跨越任务 ( 换句话说就是在变量用完前会调用 task_switch() 函数的 ) 变量通通改成静态 (static) 即可 . 这里要对初学者提一下 ," 静态 "你可以理解为
40、"全局 ", 因为它的地址空间一直保留 , 但它又不是全局, 它只能在定义它的那个花括号对 里访问.静态变量有个副作用 , 就是即使函数退出了 , 仍会占着内存 . 所以写任务函数的时候, 尽量在变量作用域结束后才切换任务, 除非这个变量的作用域很长( 时间上长 ), 会影响到其它任务的实时性 . 只有在这种情况下才考虑在变量作用域内跨越任务, 并将变量申明为静态.事实上 , 只要编程思路比较清析, 很少有变量需要跨越任务的 . 就是说 , 静态变量并不多 .说完了 " 覆盖" 我们再说说"重入".所谓重入 , 就是一个函数在同一时刻有两个不同的进程复本. 对初学者来说可能不好理解 , 我举个例子吧:有一个函数在主程序会被调用 , 在中断里也会被调用 , 假如正当在主程序里调用时, 中断发生了 , 会发生
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年度科技展会标识设计及版权合作合同
- 二零二五年安防设备进出口贸易合同
- 中国文化经典研读坛经两则课件2
- 智能化电气设备在教育建筑中的创新应用
- 2025年征信考试题库(企业征信专题)企业信用评级标准与流程含答案
- 二零二五年度O2O共享出行服务战略合作框架协议书
- 2025版茶园土地流转及生产经营管理服务协议
- 2025年度专利申请保证担保合同协议书范本
- 2025版北京汽车租赁服务与酒店行业合作合同
- 2025版企业并购合同变更补充协议
- 胆总管结石治疗
- 超声科临床操作中的伦理与法规
- TCTBA 001-2019 非招标方式采购代理服务规范
- 2025年天水市麦积区事业单位遴选历年管理单位笔试遴选500模拟题附带答案详解
- 非甾体抗炎药围术期镇痛专家共识(2024 版)解读
- GB/T 44828-2024葡萄糖氧化酶活性检测方法
- 小数乘除法竖式计算题及答案
- 2024年三级直播销售员(高级)职业技能鉴定考试复习题库(含答案)
- Unit 1 A new start 词汇教学设计-2024-2025学年高中英语外研版必修第一册
- 异位妊娠的课件
- 血管内超声IVUS简介
评论
0/150
提交评论