嵌入式系统在我们的日常生活中广泛存在,从消费类电子、医疗设备,到汽车,工业控制,航空航天等,它们的存在已经成为我们生活中不可分割的一部分。随着技术的不断进步和客户需求的增加,嵌入式系统和软件变得越来越复杂,同时产品的开发周期变得越来越短。如何在短时间内开发出高质量的软件对产品的成功起着决定性的作用。
本文将介绍如何应用编码标准和自动化工具,提高代码质量。
代码质量总体上是指为软件编写的代码的整体优良水平,一般可以通过下面一些指标来评价代码质量:
对于嵌入式系统,代码质量更加的重要。嵌入式系统经常是处理执行关键任务功能的设备。
如果嵌入式软件存在代码质量问题,可能会导致硬件设备的故障或安全漏洞,严重影响系统的正常运行。
基于历史原因,目前在嵌入式开发中,主要还是使用C/C++高级编程语言,而C/C++是一种不安全的语言,包含大量未定义的行为,对于这些行为的不同解释,可能会导致未知或不确定的副作用,其中一部分将会转化为我们俗称的“Bug”。比如C语言标准库中的一些输入输出函数,字符串函数导致的缓冲区溢出的问题,指针未初始化风险,重复释放内存等等。
同时,软件开发执行和实施的核心是“人”,也就是开发人员,在实践过程中,开发人员可能会一次又一次无意地将相同类型的错误写入到他们的源代码中。这一结论来自各种权威机构,如NASA、贝尔实验室和MITRE,它们进行了多项调查和研究。这些研究的结果是给出了最佳编程实践或推荐的编程实践,它们可以有效识别有风险和不良的编码行为。
有许多指南和编码实践可用于检查常见错误以及如何避免这些错误来提高代码质量,其中一些技术和实践在实施过程中,成为众所周知的编码标准。编码标准是编码规则、指导方针和最佳实践的集合,它可以识别语言中容易引起Bug的行为,防止你在源代码中做可疑的事情,消除容易出现缺陷的代码结构,帮助你快速提高代码质量。
在嵌入式系统中,MISRA-C/C++,CERT-C/C++,CWE等已经成为事实标准。特别是在汽车、医疗和铁路等安全关键型应用中,被IEC 61508、EN 50128和ISO 26262 等功能安全标准所要求。
很多大公司都有相应的代码编程规范,尤其是针对C语言,但这些编程规范往往与业界的编程标准(比如MISRA C)的侧重点不同。一般公司的编程规范更加注重代码风格,比如命名,缩进,括号的使用等,来提高可读性,而业界的编程标准更偏重于代码的可维护性,可移植性,可靠性和安全性。在具体实践中,往往是需要这二者结合使用。
MISRA C由汽车工业软件可靠性协会开发。它的目的是提高嵌入式系统环境中的代码安全性、可移植性和可靠性,特别是那些用 ISO C 编程的系统。
MISRA C标准的第一版“Guidelines for the use of the C language in vehicle based software”于1998年制定,正式名称为MISRA-C:1998。2004年进行了一次更新“Guidelines for the use of the C language in critical systems”,正式名称为 MISRA-C:2004。从1998年的vehicle based software到2004年的critical systems,因为MISRA协会发现MISRA C不仅汽车行业需要,其他一些安全相关的行业也需要。最新的MISAR C标准是MISRA-C:2012。
关于MISRA C,很多开发者都有一个误解:MISRA C只适用于汽车电子嵌入式软件开发。对此,MISRA C的轮值主席Andrew Banks特意做了解释说明:虽然MISRA最开始推出的时候主要是针对汽车行业的,但由于它本身其实是在C/C++语言的基础上,加上了一些约束,去掉了一些让人容易出错的编程方法,保留了常用的写法,尽可能让开发者保持一致,提高可维护性和可移植性,从而提高安全性和可靠性,因此 MISRA 在飞机、机器人、无人机、医疗等其它的嵌入式行业也开始流行起来,成为了全球公认的嵌入式 C 编程标准。
CERT C/C++由卡内基梅隆大学软件工程研究所(SEI)的计算机紧急响应小组 (CERT)部门创建和发布,为C/C++编程语言的安全编码提供规则和建议,这些规则和建议的目标是开发安全、稳定和可靠的系统。
CWE是基于社区开发的一组影响信息安全的软件和硬件缺陷列表。它用通用语言描述和讨论软件和硬件的缺陷,可以作为缺陷识别、缓解和预防工作的公共基线标准。因此,CWE可以帮助开发人员和安全从业者检查现有软件和硬件产品的缺陷,评估针对这些缺陷的工具的覆盖率等。
编码标准的应用对提高代码质量有立竿见影的作用。在Dr. Dobbs所做的一项研究中(Code Quality Improvement | Dr Dobb's),引入编码标准进行符合性检查后,缺陷注入率降低了41%,这节省了大量测试时间,既提高了代码质量,又缩短了工程时间,从而加速了产品上市。在这项研究中,每个月的缺陷注入率是相当稳定的,直到该组织引入编码标准,然后缺陷率急速下降(见下图)。
随着对标准遵从度的提高,质量也随之提高,偏差越来越少,缺陷率直线下降。
有了编码标准后,应用标准意味着代码除了需要遵守语言本身的规则外,还需要遵守成百上千条编码标准所包含的规则和要求。理论上,我们可以通过人工来检查每条编码规则的实施情况,但对于日益复杂的软件显然是力不从心的。大量的实践表明,应用自动化工具是实施编码标准,提高代码质量的最佳路径。
自动化工具中我们最熟悉的就是编译器和链接器。高质量的编译器和链接器应支持现代编程语言,如最新的C和C++规范,报告每个构建步骤中可能出现的问题,以便它生成怀疑的警告,例如易失性变量或内存访问,其评估顺序可能会影响应用程序的逻辑。警告是第一道静态分析检查,绝不能忽视,尤其是在功能安全设置中。最好的建议是通过更改编译器设置将所有警告都视为错误来将警告转化为错误。这将迫使开发人员修复代码中的所有歧义,因为所有的警告都将作为真正的问题处理。
专用的静态分析工具基于源代码分析,可以在不执行程序的情况下发现潜在的问题,比如 IAR 提供了与 IAR Embedded Workbench 无缝集成的静态分析工具 C-STAT。这种类型的工具可以帮助你找到代码中最常见的缺陷来源,也可以帮助你找到开发人员在试图编写代码时往往不会考虑的问题,特别是当他们为了让某些功能运行而加入支撑代码时。静态分析工具确实能帮助你开发出更好的代码,因为它们强制执行编码标准。事实上,如果正在创建一个功能安全认证的应用,你可能会被建议,甚至强制要求使用静态分析工具。
此外,嵌入式软件在运行时仍然容易受到算术问题、缓冲区溢出、边界问题、堆完整性和内存泄漏的影响。一个可行的方法是在可能发生潜在错误的所有地方插入特定的检测代码或断言来检测此类错误。但是,手动添加指令来检测并以某种方式在运行时报告问题是一项非常耗时的任务。因此,使用动态或运行时分析工具来捕获和触发仅在运行时的缺陷和错误,是一个可以极大提高效率和生产力的方法。例如,在 IAR Embedded Workbench 中,开发人员可以使用运行时分析工具插件 C-RUN。
综上所述,编译器和链接器可以报告在构建中可能出现的问题,静态分析工具擅长发现一些未定义行为的缺陷,检查编码标准的符合性,而运行时分析工具擅长发现只有在执行时才会触发的缺陷。这些缺陷有时会有重叠,但有时只能在一个域或另一个域中检测到。为了尽可能提高代码质量,以及发现问题的效率,需要将几者结合使用并尽可能与开发和构建工具集成。下图矩阵代表组合不同工具时的完整缺陷覆盖率。
随着嵌入式系统的复杂性提高,对于嵌入式软件的要求也越来越高,其中最核心最根本的是代码质量,而编码标准是提高代码质量的最佳实践。遵循编码标准的最有效的方式是应用自动化工具,包括静态分析工具,运行时分析工具,这样可以有效地在开发过程中提高代码质量,既减少了项目的开发时间和成本,又提高了产品的质量和竞争力。
1.https://www.misra.org.uk/
2.https://ldra.com/ldra-blog/misra-myths-busted-1-is-misra-c-just-an-automotive-standard/
3.http://www.cert.org/
4.http://cwe.mitre.org/
5.https://www.iar.com/knowledge/learn/code-quality/everything-starts-with-code-quality/
6.https://www.iar.com/knowledge/learn/code-quality/everything-ends-with-code-quality/