中国人是惯于克勤克俭的,鲁迅先生说:“时刻是海绵里的水,挤一挤总会有的!”
领导说,鲁迅说得对!
所以,领导们常常带着期盼的神态,忽悠苦逼的软件工程师:“再多想想办法吧,嗯,MCU的主频是低了点,RAM资源是少了些,可是,考虑一下本钱,MCU仍是尽量不要换的吧?办法总比困难多,看着RAM资源如同不大够,可是换个完成方法,还能够再挤一挤的吧?鲁迅先生曾说……”
好吧,领导们必定读过华严经,深谙佛菩萨“螺蛳壳里做道场”的本事:大就是小,小就是大,巨细无二无别!嫌功用太多,RAM资源太少,多少算多呀?为啥子要生出那么多别离心撒?
可是,领导们或许不知道,鲁迅先生写错了字不叫错别字,叫“通假字”,我们写错了就是实真真实的“错别字”,并且佛菩萨的境地也是“非汝边事”。所以,用小马拉大车,在资源一般般的MCU中塞入尽或许多的代码,完成那么多功用,还能让这些模块合作无间地密切工作,真实不是我等的境地了!
这不,搭档小王又找我来诉苦了。
1
“马步君,救救我吧。快要被领导摧残疯了,这就是个16位的单片机,RAM一共512个字节。留给仓库256个字节,剩余的就只需256字节了,哪能完成那么多功用呢?可是领导说干嘛留给仓库256个字节,仓库留少一点RAM不就够用了吗?”
说着说着,小王更加地怒火中烧了:“分明有个管脚兼容的芯片,RAM有1k字节,可是领导就是不让换。说让仓库留少一点,哼,他知道个屁!滚粗,仓库不可的话体系会跑飞的呀!”
看着小王蜡黄黄的脸蛋和红通通的眼睛,我心下有些不忍:‘千般皆苦,做人最苦,难怪如来说为不幸愍者呀!’可是,领导说的也不无道理,关于仓库该设置多少,许多人都是稀里糊涂,又有多少人能够弄得了解呢?所以我竟而给领导辩解了起来:“或许领导说得对吧,由于你的确不知道该给仓库留多少空间吧?”
我一边小心谨慎地说着,一边看着小王的脸渐渐地耷拉了下来,甚而就要拉到地上了。所以我急忙提起千般的精力找补一番,给他讲起MCU中RAM资源和仓库分配的矛盾性来:
“RAM资源的确很重要,领导的意思应该是说你对它的分配要照顾到使用、体系仓库两方面的需求,不可有所偏颇。
在MCU的地址空间中,RAM是接连分配一段线性地址空间,使用中用到的全局变量、中止和体系调用用到的栈、动态分配用到的堆都要分配在这段有限的线性空间内,当然你能够挑选不必‘堆’。不过,假如有所充裕,或许的确需求,你还得把存在程序存储空间中的一段代码复制到RAM空间内运转,以加速程序的运转速度,进步体系实时性。
所以,RAM资源的确是有限,不或许也不应该盲目得为仓库分配太大的尺度。不过话又说回来,假如仓库设置地过小也不可,由于设置过小的话,一旦程序设计得不合理就很简单出问题。比方在函数调用中子函数中的局部变量太多、中止优先级设置得不合理导致凹凸中止间的嵌套、中止ISR程序过长导致本中止被嵌套,或许呈现函数调用层次过深等程序设计不当之处都或许导致仓库溢出,改动接近仓库的RAM空间中的内容,然后形成程序运转反常,产生毛病乃至导致重大事故。
从这个视点来说,在必定程度上,仓库设置得大一些,有利于补偿程序设计的缺点。话再说回来,程序设计地很完美,就不需求设置那么大的仓库。归结究竟,这就是个平衡木啊!”
跟小王进行了这段科普后,他着实有些懵圈了。所以我把他晾在一边,忙活起了自己的事儿。
我想,上面那番话够他消化一段时刻的了。
2
快到饭点了,工作室里忽然热闹了起来,有人在大声讲电话,有人被踩了尾巴似的叫上一声,然后戛然而止,就像被一把剪刀剪断了声线一般,有人开端四处走动串联,可是我却感到背面有一种异常的幽静!公然,一回头,小王又找上门来了。
“马步君,你方才说的是不错,仓库不能设置得过大,也不能设置得过小,可是这如同等于什么也没有说相同嘛。归根究竟,我该怎样设置仓库的巨细呢?”缓过神来的小王,忽然发现我仅仅专业性地描绘了问题,却没有给出问题的答案。
“孺子可教也,”小王的提问让我不由有些傲然,我一边向他投去欣赏的目光,一边心下思忖该怎样样答复。思量顷刻,我又敞开了说教形式:
“能够经过静态剖析的方法确认仓库空间的尺度。你需求依据源程序中每个函数的局部变量巨细确认每个函数的仓库使用量,然后依据编译器生成的函数调用列表为每个函数树立调用树,查看每棵调用树,确认从树根到树叶的调用途径的仓库使用量,从中选出最大仓库使用量,一起,还要仔细剖析体系用到的一切中止,确认中止服务程序的仓库使用量。”
看着他再次堕入懵圈状况,我满足地址了允许,兴起腮帮子持续说教,
“可是,除了我们自己写的程序,你所调用的C规范库函数以及大值整数的乘除、浮点运算等对应的运转库函数也会耗费仓库,它们的仓库使用量详细是多少我也不是很清楚,可是应该能够查得到。讲到这儿你也看到了,这种静态剖析方法对开发者的技术水平、对产品代码的了解程度要求十分高,得到的数据并不完善,并且这种方法依赖于详细的使用和源程序完成方法,所以,好麻烦!”
被提到置疑人生的小王再次锁紧了眉头,抿着嘴唇一言不发,他在想什么我不清楚,可是我想:“依照我方才的说法,我不是也不知道该怎样设置仓库巨细的嘛?哎,做人难,做嵌入式软件工程师更难啊!”
3
我本认为这件事到此结束了,没曾想吃完饭后,小王又找上门来了,“马步君,你方才说了,经过静态剖析判别仓库使用量对程序员要求很高,并且不通用,那么,有没有一种动态的判别方法呢?”
“当然有了,能够在链接文件中,对RAM的空间分配做手脚。”我再次侃侃而谈起来,这边厢我吐沫飞溅,那儿厢小王两眼放光。各位看官且先不要觉得笔者的思想真实灵敏、脑路不得了的灵光,而对笔者投来钦敬的目光。实际上,就在吃饭的空当,我就在苦苦地思索,究竟该怎样,仓库的空间分配才算恰当。
假如不在链接文件中做任何设置,RAM就是仓库区+全局变量区,这样一来,仓库区以下就是全局变量区,仓库的成长方向为自上而下,即向着RAM地址减小的方向增加,仓库溢出时改动全局变量的值,可是许多情况下,你底子认识不到程序溢出,只需在特别的触发条件下程序运转某个功用时,你才或许认识到不对劲。
所以,为了第一时刻就查看到仓库溢出,要参加一个紧邻仓库区的新区,这个新区叫‘仓库溢出缓冲区’。想一想哈,这时仓库溢出时就会改动‘仓库溢出缓冲区’的数据,只需我上电初始化时将‘仓库溢出缓冲区’初始化为固定数据,然后守时查询这个新区中的数据,就能判别仓库是否溢出,并且能够判别这一段时刻内的最大仓库使用量。”
我渐渐着解说着自己的思路,等着小王渐渐跟上来。过了一瞬间,小王又猝不及防地提问了:“假如仓库设置地比较大,不会产生溢出,那这个‘仓库溢出缓冲区’也起不到什么效果,只会白白浪费RAM资源啊!”
好吧,我供认,其时的确被他问住了,可是,已然之前的思路现已翻开,再打个小补丁就不算什么难事了。我思量顷刻,就给出了让他满足的答案:
“能够在MCU上电初始化时,将仓库区和仓库溢出缓冲区的数据悉数初始化为一个固定数据,比方0xa5,将最大仓库使用量记为stack_max,然后用一个周期守时器守时读取仓库溢出缓冲区和仓库区的数据,就能够判别仓库设置是否过大。
并且,第2次读取这两个区的数据时,从stack_max个数据后开端读取即可,比方上周期计算到仓库用到100个字节,stack_max=100,下个周期从第101个字节开端读起就能够了。
假如你开端设置仓库为384个字节,跑了一天后,发现stack_max=210,那就把仓库设置为256就能够了。这样就能处理你的问题-科学合理地缩小仓库分配了!”
4
过了几天,小王总算发现,‘本来’自己的程序用到的仓库历来都不会超越130个字节,所以他乖乖地改小了仓库,把空出来的100来个字节都分给了全局变量,RAM一会儿捉襟见肘了,他很开心肠对我说:看来仍是不要动辄滚粗,要先看看仓库是否真的溢出!