节省内存的嵌入式软件设计技巧

现在新买的安卓千元机都是2G内存的了,我们还要绞尽脑汁地省内存?是的,那是高端处理器的特色,咱们这里讲的是资源紧缺型的嵌入式系统设计方法。一般主控是单片机控制器的电子产品的成本跟内存的关系可是成正比的,尤其在SOC芯片设计时是固件开发需要重点关注的。大量量产前要确定内置SRAM的大小,而且是在满足功能需求的情况下越小越好。这就需要考究软件系统的设计和编程开发的技能了。这里仅就我个人的工作经验来总结,涉及的是音视频多媒体电子产品,类似系统一般都会自行定制操作系统,驱动、中间件和应用等模块都有,所谓麻雀虽小,五脏俱全。

一、内存块分时复用

分时复用即对代码进行分块(Bank)管理。它的设计需求来源于:

1. 很多电子产品并不是像现在的安卓手机一样同时跑多个应用,顶多就听歌时浏览图片而已,非智能手机也是如此。但我们也会看到电子产品里面有有很多的应用,如设置、电子书、电话本、录音啊等等。因此,不同时运行的应用占用同一块内存空间理所当然。

2. 驱动空间。有很多的驱动并不同时使用,如听FM时是FM驱动,听歌又是使用解码器,所以很多驱动也是可以服用同一块空间的。

3. 中间件的复用。如UI、硬件驱动的再次封装使用等等,其由对应的应用直接调用,一般也存在复用的需求。

4. 数据段的复用。应用和驱动都有数据,同样有复用的场景需求。

理论上驱动和代码也可以服用空间的,但需要考虑的细节太多,而且这样做扩展性不好,所以应用一般是不会跟驱动复用空间的。一般较为粗糙地将软件系统分为以下几个部分:启动、驱动、操作系统、中间件、应用等层次。启动为一次性执行,不需太多考虑复用的空间。操作系统一般有常驻内存的需求,如中断管理、时间管理、调度管理、模块代码管理、虚拟文件系统等等,当然操作系统的一部分功能并不需要常驻内存,主要是一些调用频率较小的一些接口,如驱动装卸载、应用初始化等模块。不需常驻内存的一些接口实现也可以跟驱动复用空间。

咱们不妨比较一下高端 处理器的内存管理单元的功能,内存管理单元实现内存管理有两个部分,包括硬件TLB模块和软件的页表,硬件TLB是自动将虚拟地址的高N位匹配成物理内存的高N位,匹配是根据页表(TLB是页表的cache)进行。可以认为页表是虚拟-物理映射的索引表。高端处理器一般所带的内存都是M级别以上,往往是SDRAM,而不是内置 SRAM了。一般也会用支持多进程的操作系统,即同时支持多个应用在跑。而这多个应用能够使用的虚拟地址空间和物理地址空间都是整个空间(可能会划掉一部分用于操作系统,linux就是这样),也就是其在整个地址空间中进行分时复用。 而我们上面所讲的代码分块管理只是参考了MMU的设计思想,其分块是在一定的空间中进行的,而且应用和驱动的分块空间是分开的。

二、代码分块的技巧

第一点是分块管理的需求和大致的原则,但是如何分块,块大小的设计极为考虑系统架构师的功力。块设大了浪费,小了会导致代码切换频繁效率低下。既然都是RAM,有时数据可以跟代码段放到同一个块中,而没有必要另加一块数据块。当然这些细节需要综合评估并加以详细设计。在成本敏感的电子产品中,这些技巧需要努力挖掘发挥。

三、ROM代替RAM

这只是从成本的角度去节省内存资源,有些代码需要常驻内容,但其内容并不会随着版本的更新而发生变化,如上节所讲操作系统的调度管理等,可以考虑将这些代码固化到ROM中。理论上操作系统大部分需要常驻内存的代码都可以固化。RAM和ROM同样大小的成本比大概是6:1,因此使用ROM也能大幅降低成本。

四、系统移植时砍掉不需要的模块

这是操作系统设计人员务必要考虑的。每个产品都有独有的功能,而底层操作系统具有普适性,在资源紧缺型系统中,砍掉不必要的模块是非常明智的。

五、操作系统定制

也可以称为改进操作系统,我们所阐述的系统一般都是封闭系统,只要能高效地实现功能,我们可以任意改动系统中所有的代码。例如对于可执行的ELF文件,操作系统如果按标准的流程要解析完ELF文件再加载,但不仅需要很多的内存资料,而且也效率低下。ELF有关加载和执行最重要的就是.CODE、.DATA、.CONST、.BSS等段信息,我们完全可以离线抽取出来生成一个新的简单的定制文件格式,操作系统只需解析这个简单的文件就可以了。这样做不仅节省内存,也能节省外存储空间。

六、 编程技巧

这个需要平时的积累。例如,在变量的排列方面,我们都知道编译会考虑对齐。

char a;int b; char c;这样定义变量的次序需要的内存是比 char a; char c; int b;要大的。

七、算法设计

好的算法一般会是轻巧的,效率高的。

八、代码编译优化

编译时选择优化级别高的,这样生成代码大小有有大规模的减小。

九、编译指令模式

如arm里面选thumb指令,mips选择mips16e,这是由体系结构所决定的,体系结构也是为了考虑节省代码空间资源而设计了16位的指令模式,而这些CPU的字长往往是32位。这种方式能减少30%左右的代码量。

  十、 栈空间的规划

每个线程都会有自己的栈,而每个线程的栈都应该根据其线程的调用深度来具体设定,像UCOS就有一个栈使用率的任务,我们不妨借用这种思路来看看某个线程最终的栈深度。

设定独立的中断栈,可以避免每个任务栈都要给中断预留栈空间。

扁平的函数调用方法用栈一般要比纵向的函数调用小。嵌入式开发有时为了效率和资源,不应该把代码分块分得太细,函数一大摞,既增加代码量和栈,也降低运行效率。

十一、合理规划内存空间,调整好链接文件,尽可能做到已有物理空间的高度利用。

例如,我们规划空间时往往代码段和数据段分开,但实际的代码段可能又用不完,这时就可以把一部分变量定位到代码段之后。

十二、善于利用链接的段属性

利如uboot的命令格式,每个命令格式都是一个数据结构cmd_tbl_s,有名称、执行函数、帮助提示信息等等。考虑到命令的管理,我们自然会想到结构数组,但是数组的大小怎么设置呢?设置大了浪费,设置刚刚够,那增加一个命令又得改大小。uboot巧妙地运用了链接的段属性,只要是命令就加上section (".u_boot_cmd"),那所有命令自然就放到这个段里面了,需要查询命令就遍历这个section就可以了。linux里面大量应用了链接段属性技术。

时间: 2024-10-18 15:17:59

节省内存的嵌入式软件设计技巧的相关文章

用户界面设计经验分享:界面设计技巧分享

如此有用的文章我已记不得是什么时候发现的了,但在看完的那一刻便想将之翻译,分享给大家自己也受用. 时间过了很久,来到了2014年,终于静下心来花了大把时间连同图片一起译成了中文.像我这样业余的翻译六级分数只够及格的程序员,不敢说做到信雅达,但求意思到位. 1 尽量使用单列而不是多列布局 单列布局能够让对全局有更好的掌控.同时用户也可以一目了然内容.而多列而已则会有分散用户注意力的风险使你的主旨无法很好表达.最好的做法是用一个有逻辑的叙述来引导用户并且在文末给出你的操作按钮. 2 放出礼品往往更具

Android仿微信录音功能,自定义控件的设计技巧

欢迎各位加入我的Android开发群[257053751] 最近由于需要做一个录音功能(/嘘 悄悄透露一下,千万别告诉红薯,就是新版本的OSC客户端噢),起初打算采用仿微信的录音方式,最后又改成了QQ的录音方式,之前的微信录音控件也就白写了[大哭].之前有很多朋友在问我自定义控件应该怎么学习,遂正好拿出来讲讲喽,没来得及截效果图,大家就自己脑补一下微信发语音时的样子吧. 所谓自定义控件其实就是由于系统SDK无法完成需要的功能时,通过自己扩展系统组件达到完成所需功能做出的控件. Android自定

浅谈数据库设计技巧(转)

说到数据库,我认为不能不先谈数据结构.1996年,在我初入大学学习计算机编程时,当时的老师就告诉我们说:计算机程序=数据结构+算法.尽管现在的程序开发已由面向过程为主逐步过渡到面向对象为主,但我还是深深赞同8年前老师的告诉我们的公式:计算机程序=数据结构+算法.面向对象的程序开发,要做的第一件事就是,先分析整个程序中需处理的数据,从中提取出抽象模板,以这个抽象模板设计类,再在其中逐步添加处理其数据的函数(即算法),最后,再给类中的数据成员和函数划分访问权限,从而实现封装. 数据库的最初雏形据说源

数据库设计技巧

1. 原始单据与实体之间的关系 可以是一对一.一对多.多对多的关系.在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体.在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单据对应多个实体,或多张原始单据对应一个实体.这里的实体可以理解为基本表.明确这种对应关系后,对我们设计录入界面大有好处. [例1]:一份员工履历资料,在人力资源信息系统中,就对应三个基本表:员工基本情况表.社会关系表.工作简历表.这就是"一张原始单据对应多个实体"的典型例子. 2. 主键与外键

【原创】一个支持极限大小的数组MaxArray,且节省内存

大家好,我写了一个支持极限大小的数组MaxArray,很有用的,希望大家喜欢~~ 问:.net类库不是自带了一个吗,干嘛还要自己写一个?好在哪里? 答:数组可以在创建后立即访问范围内的任意索引位置,而不需要依次添加,可以跳跃添加,但它的缺点就是创建时立即分配全部内存,比如你连续新建几个int[] arr=new int[int.maxvalue]这样的极限大的数组,会遇到内存溢出异常. 问:要节省内存,可以用ArrayList或List<T>这些,干嘛非得自己写一个? 答:arraylist或

C++STL内存配置的设计思想与关键源码分析

说明:我认为要读懂STL中allocator部分的源码,并汲取它的思想,至少以下几点知识你要了解:operator new和operator delete.handler函数以及一点模板知识.否则,下面你很可能看不大明白,补充点知识再学习STL源码比较好. 下面会结合关键源码分析C++STL(SGI版本)的内存配置器设计思想.关键词既然是“思想”,所以重点也就呼之欲出了. 1.allocator的简短介绍 我阅读的源码是SGI公司的版本,也是看起来最清楚的版本,各种命名最容易让人看懂.alloc

界面设计技巧

如此有用的文章我已记不得是什么时候发现的了,但在看完的那一刻便想将之翻译,分享给大家自己也受用. 时间过了很久,来到了2014年,终于静下心来花了大把时间连同图片一起译成了中文.像我这样业余的翻译六级分数只够及格的程序员,不敢说做到信雅达,但求意思到位. 1 尽量使用单列而不是多列布局 单列布局能够让对全局有更好的掌控.同时用户也可以一目了然内容.而多列而已则会有分散用户注意力的风险使你的主旨无法很好表达.最好的做法是用一个有逻辑的叙述来引导用户并且在文末给出你的操作按钮. 2 放出礼品往往更具

C语言-第36课 - 函数递归与函数设计技巧

第36课 - 函数递归与函数设计技巧 一. 递归 递归概述 (1) 递归是数学领域中的概念在程序设计中的应用. (2) 递归是一种强有力的程序设计的方法. (3) 递归的本质为函数内部在适当的时候调用自身. 组成部分 (1)递归点:以不同参数调用自身. (2)出口:不在递归调用 下面就是求一个数的阶乘的函数: #include <stdio.h> int func(int x) { if( x > 1 ) { return x * func(x - 1);  //递归点 } else {

硬件工程师需要知道的8个软件设计技巧

嵌入式系统设计不仅要了解硬件还应该了解它与软件之间的相互影响和作用.硬件设计需要一定的设计范例,这点对于软件设计却不那么适用.如何从单纯的硬件设计过渡到硬软结合的设计,在你着手开发软件时需注意以下八个软件设计技巧. ??1.设计控制流程图 ? ? ? ??工程师进行到开发软件这一步时会情不自禁地开始书写代码.这种思维定势就像在原理图还未完成之前就开始尝试画PCB.当着手开发软件时,克制写代码的冲动,取而代之的应该是软件流程结构图表的设计,这点非常重要.流程图能清晰地呈现给开发人员软件的各个需要的