软件研发
1、编码规范和设计架构
编写软件,特别是代码量稍庞大一点的软件,一定要注意编码规范和架构的设计。在项目的前期做好需求分析,模块的划分,组成关系,通信的接口等等。这些工作做得越详细,越到位,后期的编码工作就会越顺利,出错的概率会很大程度的降低。

多年前曾参与一个嵌入式软件的设计,前期的文档编写和讨论就用了一个月的时间,其中明确了整个软件的功能,模块组成,结构关系和通信接口等,甚至讨论到了接口函数的形式,代码的实现细节。当时觉得很浪费时间,规划了太久,可是到了编代码的阶段才发现,进展的很顺利,短时间内就完成了。测试上也没有出现严重的问题。从那以后,也意识到了软件前期规划的重要性,规划的越仔细,后期的调试改进工作越顺利。
软件前期设计重要性

2、变量命名和初始化
变量命名这个问题相信很多公司都有自己的一套规范,比如匈牙利、大小驼峰等,这些都是经常听说的命名方式,其实不论哪种命名,其根本目的是让程序能够被人读懂和理解,统一代码风格,方便继承和管理。
变量的初始化,经常看到定义时没有初值,这个如果是全局变量(当然不建议使用全局变量)可能由编译器和运行的OS等帮助清零(BSS),但如果是局部变量,就很可能是一个垃圾的值,一旦忘记赋值(或者赋值的语句没有执行),引用起来就可能引起意外的发生(最好的是挂在当前的函数或者任务或者线程,如果是挂在了别的线程中,就会有种莫名其妙,偶发崩溃,非常棘手)。再设想C语言中的指针没有赋初值呢?野指针乱飞的后果也会很严重。
记得有些公司在招聘时都会出一些基础的面试或者笔试题目,其中就有对于定义变量(尤其是指针变量)赋初值的考察。工作时间越久的,这方面的考察越仔细。可能大家都不能忍受庞大的软件运行时,出故障的原因竟是一个变量没有赋初值吧···
变量初始化重要性
3、函数参数有效性验证
不同的项目有不同的设计要求,比如要求特别严格的电力、轨道交通、航空航天等领域,有些软件设计时函数要求只有一个出口,保证安全可控。但多数的代码设计时还是会在函数入口处放置一个return语句(如果入口参数检测不通过)。
个人认为很有必要在函数的入口处加入参数检验的代码,如果输入异常,立即返回错误信息,提醒调用者输入参数有误,执行效率高些(当然也可以在函数内部标记错误信息,等待函数体执行完毕,统一出口,返回错误信息,效率也不会差很多)。
参数有效性验证
4、函数的返回值
函数的返回值的设计和调用检测都很重要,设计者可以用于标记函数体内重要执行过程的状态信息,调用者可以清楚当前函数的执行结果,从而决策程序的下一步行为。
曾经碰到过程序设计者没有检测API的返回值,结果运行了很久,都不知道出错在哪里,因为调试的代码中没有错误检测的信息。只关注if(success == API)而没有else的情况很糟糕,连返回值是什么都看不到,调试很不方便。
调用API注意细节
5、分支结构考虑完全
程序设计时尽量保证结构的完整性,任何时候都不要设想没有default或者else的情况发生,一切皆有可能的。程序设计时建议在else或者defalut中加入调试信息,这样在Debug时,单步调试或者加入断点等都很有帮助。
软件分支结构设计完整
6、内存泄露
偶发的软件死机很可能是两种原因:一个是内存泄露,另外一个是线程锁。内存泄露会导致莫名其妙的死机问题,当事发现场就死机还好,如果事发现场没有死机,而是将错误蔓延到其他线程(多线程编程模型下),出了故障会很难定位和调试。
多线程编程下很容易出现线程内申请一个定长数组,然后就不去动它的长度,以后不断的利用此数组作为缓冲区,处理传递数据,每次进入此线程都会刷新此缓冲区内的数据,最可怕的就是后面处理数据时拷贝数据长度超出此数组的最大容量值,造成栈溢出。很多情况下这种错误并不会立即造成死机,程序运行只是结果不对而已。故关于数组的copy或赋值操作务必注意其最大长度,务必对copy的长度做限定。
多线程编程下还有一种任务栈溢出的问题,常常在创建任务时会指定固定长度的任务栈,相信在创建任务时,都会申请足够的栈空间,自己也清楚任务需要处理的数据量,可在调试一段时间后,很可能会有新的需求加入,比如新建一个结构或者临时数组,一定要注意任务栈的使用情况,运行时监测任务栈的余量。任务栈溢出,如果操作系统监测到,会给出警告,进而停止软件的运行,但若操作系统不检测,就很危险,结果也是不可预测的。
内存泄漏
7、函数的可重入性,线程安全
说到线程安全,可重入都会考虑到嵌入式操作系统下的编程,其实在MCU或者单片机、微控制器的编程下也有多线程的影子,主函数main中的while(1)循环和各种中断处理函数之间也算作多线程的操作,当多个中断和while(1)下处理同一资源时应该加入保护机制。同样在多线程的操作系统,甚至多核多线程编程模型下更要注意对于竞争资源的保护机制,同时注意函数的可重入性,设计时是否用到了全局的元素,局部静态变量等等。特别是习惯裸机编程的设计移植到多任务系统,或者单核多线程编程移植到多核SMP编程环境下,更需要注意这些。
软件设计安全性
8、加入必要的调试手段
软件的可测试性一直是作为考量软件设计的很重要的因素,很多嵌入式软件都运行在微处理器之中,有的工作在飞机、铁路、卫星、甚至是飞船之上,如何在软件运行出故障时监测和定位故障点,是非常重要的,想想当年火星探测的设备,VxWorks系统在出现故障时,从地面的定位和升级手段,是多么的重要。所以很有必要在软件的设计之初,就将测试和协助解决软件故障的功能加入进去,在后期的测试和实际运行环境中一定有其大展拳脚的时候。
软件测试技术
9、注释的必要性
软件的设计标准总一定都离不开对于注释的要求,可见注释对于软件编写的重要性。所谓好记性不如烂笔头,当觉得自己编写的这段代码很漂亮,写的很出色时,别忘了记录下自己骄傲的时刻,写上时间作者和你实现的重要功能,注释不是直译代码的表面的意思,而是对于现实事物的表达。
如果你的代码里充满了j=i+3; j=2i+3+1 ···如果不进行充分的注释,一年后当你再次看到自己的设计时,你会是什么样的感受呢?当别人看到这段代码绞尽脑汁的思考时又是怎样的体验?
代码注释必要性
以上是自己对于嵌入式软件设计的思考和总结,软件编写出来容易,稳定工作下去,却非容易的事,好的设计方法和技巧,能够在软件设计之初就规避大部分的问题出现,防患于未然,防御性的编程思想,值得我们每一个软件设计人员学习。