本帖最后由 GorgonMeducer 于 2020-6-5 04:53 编辑
【序】 不知道你发现没有,平时我们讨论嵌入式软件开发时总绕不开与实时性(Real Time)相关的话题。相信不少朋友和我一样是通过实时性操作系统(Real Time Operating System, RTOS)第一次接
触到实时性概念的——我记得那还是大学时代、参加机器人竞赛的时候。工作以后自信地以为加深了
不少对实时性的本质认识——现在看来其实还未摸到门道。就这样浑浑噩噩一直到毕业后的第八年,
因为工作变动的原因,我被迫要在一周内要做一个实时性原理相关的研究报告,也就在那时,我体会
到了疯狂练功走火入魔的感觉:走路在思考、吃饭在看资料、头一直发烧一样的微微发热、甚至连睡
觉都在梦中推演模型——头发一把一把的掉,幸好有截稿时间,否则真的要秃了。
也就是经过那一次,我突然发现自己之前对实时性的认知可谓徒有其表,甚至从未做对实时性模型 本身的定量分析——所幸,那次研究报告如期交付,工作变动也如愿以偿。然而,3年后我发现“我 又双叕天真了”——那是有一次,我正跟人讨论嵌入式基本范式,就突然一个瞬间,脑海中原本毫不 相关的一些模型猛地被联系到了一起(音效请脑补):
我甚至本能的立即意识到:之前自己在某篇文章中“言之凿凿”的推论过程其实存在巨大漏洞——当然, 那本书从未出版过,而且会闲到对我进行深究的人估计也没有几个。
今天,即便我非常确信——在前方至少还有几道数学的深谷阻碍着我触碰“实时性”的圣杯——然而我并
不是计算机科学家,现有结论对我来说已经足够装逼。回头看来,根据
我的经验以及与朋友讨论的结果,大致认为大部分人对实时性的认知过程通常会分以下几个阶段:
机下的“前后台系统”;
的朋友对RTOS充满了好奇,以编写自己的RTOS为“ 终(zhong)极(er)目标”;
意味着越快越好,但也认为“在可能的情况下”“快一点响应事件没啥坏处”;这一阶段的朋友可能已
经可以在裸机和RTOS之间自由的反复横跳,无论是裸机下的状态机还是RTOS下的线程都已了如指
掌、任务间通信更是游刃有余;
关于实时性认知的秘密;到达这一阶段的朋友通常觉得没必要、也没心思继续思考实时性更本质的
数学意义——因为此时获得的结论已经足够了应付几乎所有的工程开发了。顺便说一下,我就在这
里。
只是暴露年龄的玩笑,但肯定可以水几篇SCI论文了……
在理解了实时性的模型以后,我(本能的排除了自己比较笨这个可能性,然后)意识到:其实这一过程完
全没必要如此漫长和曲折——很多结论和道理是如此简单——不仅书本上有,而且解释和学习起来都不费
什么力气。可能这就是“挠破头”想通某个道理之后,回头再看时忍不住要“苦笑”时的感受吧。
按照约定,为了将经验和知识分享给大家,从本文开始,我将以几篇文章的篇幅:从基础模型开始,由
浅入深、由理论到实践,推演关于实时性的几个重要结论——从而直接跳跃到Lv4的认知阶段。如果你
看了这个系列后有什么话想说的、想问的,还请在评论区写下您的留言。求评论、求转发、求收藏。
【击碎 “唯快不破” 的神话】
图1展示了一个标准的实时性模型:
规定一个死线(Dead Line),即:一旦事件发生了,如果不在这个死线之前完成整个对事件的处理,
就视作失败;
时刻完成了对事件的处理,才能称为实时性得到了满足;
图1 实时性基本模型
考虑一个有趣的问题:对一个实时性任务来说,实时性窗口内的时间,其价值是一样的么?换句话说,横
竖处理事件消耗的时间是不变的,早点做迟点做都是做,有什么区别么?
图2 实时性窗口内不同时间段完成事件响应
对比图2所示的三种情况,可以很清楚的得出结论:理论上,从满足实时性的角度出发,在时间窗口内任 意时段完成对事件的处理都满足实时性要求;早做没有任何额外的好处,“踩着上课铃到校”也没有任何惩
你说“我不管,我不管”,既然什么时候做都一样为什么不能“尽早做”?“你也说了尽早做没啥不好”,“中断 来了,服务程序执行了,我想让它迟点执行也做不到啊?”
为了回答这个问题,我们不讲大道理,先看一个常见的例子:
- void main(void)
- {
- ...
- while(1) {
- task_a();
- task_b();
- task_c();
- }
- }
复制代码
反之则立即退出任务函数——释放处理器;
3ms和0.4ms。如图3所示:
图3 三个事件的实时性窗口和事件处理时间示意图 事件立即处理,绝不迟延”!
基于上述事实,容易发现:假如某一时刻,A、B、C三个函数都处于触发状态(等待处理的状态),而超 级循环恰巧进入task_a()执行——这种情况其实比想象中容易发生,比如从task_a()退出到task_c()执行完 成期间,事件A触发了;从task_b()退出到task_c()执行完成期间,事件B触发了;在task_c退出()之后恰巧 事件C又触发了……此时,任务A会立即响应,消耗4ms的时间来完成事件处理;当从task_a()函数退出时, 剩余给task_b()的时间窗口只有2ms(6ms-4ms),而事件B的处理函数需要3ms——显然事件B的实时性是 无法得到保证的——当然事件C已经死得透透了……
图 4 “越快处理越好” 导致其它任务无法满足实时性要求 通过上面的例子,我们知道“越快处理越好”是值得反思的——至少会存在情况导致系统在某些时刻无法
满足实时性要求;那么从模型上来说,如何理解这一现象呢?让我们重新来看图1所示的模型:
实际上,如果单纯从一个实时性任务自身出发来看,的确在实时性窗口内,任意时间完成事件的处理
都是一样的;然而,通过前面的举例我们其实可以发现,当一个系统中存在多个实时性任务时,虽然
一个实时性窗口内的任意时间对任务自己都是等价的,但越靠前的时间对“别人”来说是越宝贵的:
走——典型的损人不利己;
的时间,实际上是一种“利他主义”;
如果数学证明整个系统一定存在一个方案来满足所有任务的实时性需求,那么利他策略一定能找到
这样的解决方案。
图 5 一种可能的解决方案(不是唯一)
作为一个系统开发者,我们显然是需要从全局考虑的,因此完全没有必要从单个实时性任务的自私
视角来看问题,因此结论就变得更为直接:实时性窗口内越靠前的时间价值越高,从总体上来看“单
纯”越快越好的策略对实时性是有害的。既然单纯的“越快越好”不可取,且“实时性窗口
内”越靠前的时间越有价值,是否意味着,其实“越靠后越好呢”?
为了验证另外一个极端“越慢越好(越靠后越好)”是否是正确的,我们不妨以同样的例子来推演一下,
仅仅更新task_a()、task_b()和task_c()的执行策略:从“越快越好”变为“越慢越好”——这实际上意
味着:
根据这一算法,我们推演得到以下的尴尬情形:
图 6 过于谦让的后果…… 不妨分析下过程:首先,task_a()执行,在了解到距离自己的最后时刻还有6ms的实时后毅然的决定 把宝贵的时间留给他人;于是,CPU来到了下一个任务函数,基于类似的原因,task_b()也摆摆手…… 最终第一轮三个任务都决定再等一等……
如此谦让(浪费)了3ms以后,任务B终于决定下场——在执行了3ms任务处理后,成功的将随后的任 务C逼上了绝路……随着A的沦陷,大型翻车现场成就达成……
从结论上看,另外一个极端“越慢越好”也是走不通的。那么究竟如何才能从模型分析的角度出发得
出一个令人信服的、容易理解的、满足所有任务实时性需求的方法呢?关于这一点,我们下次再聊。
【小结】 从系统全局来看,实时性窗口内的时间越靠前越有价值,应该尽可能留给别的更紧急的任务来使用。
事件发生时“越快处理越好”的策略直接占用了它人的“生命线”——当所有的任务都试图“损人不利己”
时,那么整个系统没有一个任务是可以保证自己的实时性不被它人破坏的。从结论上看简单的“越快
越好”策略在实时性系统中是不允许的。
|