百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

嵌入式开发 技能(嵌入式开发技能大赛)

toyiye 2024-08-10 21:31 12 浏览 0 评论

在嵌入式软件开发过程中,一般来说,测试和编码所花费的时间是3:1(实际上可能更多)。随着你的编程和测试水平的提高,这个比例会不断降低,但无论如何,软件测试对于普通人来说都是非常重要的。

很多年前,为了更深入地了解嵌入式系统,一位开发人员向 Oracle 提出了一个问题:我如何才能知道和理解我的系统在做什么?Oracle 对这个问题感到有些惊讶,因为当时没有人问过这个问题,而当代嵌入式开发人员最常问的问题都是一些肤浅的问题,例如“我怎样才能让程序运行得更快?”和“哪个编译器最好?”

因此,面对这个不同寻常却又成熟的问题,Oracle 欣喜不已,认真地回答道:你的问题非常深刻和成熟,因为只有不断加深理解,才能不断提高水平。为了鼓励这位执着的程序员,Oracle 告诉他嵌入式软件开发和测试的 10 个秘密:

1. 知道如何使用工具

嵌入式系统通常对可靠性要求较高,嵌入式系统安全性一旦失效,可能造成灾难性的后果,即使是非安全系统,也会因大规模生产而造成严重的经济损失。这就要求对嵌入式系统,包括嵌入式软件进行严格的测试、确认和验证。随着越来越多的领域使用软件和微处理器来控制各种嵌入式设备,对日益复杂的嵌入式软件进行快速有效的测试就显得更加重要。

就像修车需要工具一样,一个好的程序员应该能够熟练的使用各种软件工具。不同的工具有不同的使用范围,不同的功能。利用这些工具,你可以看到你的系统在做什么,占用了哪些资源,在和哪些外部的东西打交道。困扰你好几天的问题,也许用某种工具就可以轻松解决,可惜你就是不知道。那么为什么那么多人总是在挣扎了很久之后,才想到用测试工具呢?原因有很多,主要有两个,一个是害怕,一个是懒惰。害怕是因为在代码中加入测试工具或者测试模块需要技巧,可能会引入新的错误,所以总喜欢希望通过不断修改、重新编译代码来消除bug,但于事无补。懒惰是因为习惯了使用printf等简单的测试方法。下面介绍一些常用的嵌入式测试工具。

源代码级调试器

此类调试器一般提供单步或者多步调试、断点设置、内存检测、变量查看等功能,是嵌入式调试最基本、最有效的调试手段,例如VxWorks TornadoII提供的gdb就属于此类。

简单实用的打印显示工具[printf]

printf 或者类似的打印显示工具可能是最灵活、最简单的调试工具了,在代码执行过程中打印各种变量,可以让你了解代码执行的状态。但是 printf 会干扰代码的正常执行(一般 printf 占用 CPU 的时间比较长),所以需要谨慎使用,最好设置打印开关来控制打印。

ICE 或 JTAG 调试器 [在线仿真器]

ICE 是用来模拟 CPU 内核的设备,它可以在不干扰运算器正常运行的情况下实时检测 CPU 内部的工作情况,还可以提供桌面调试软件提供的功能:复杂的条件断点、高级实时跟踪、性能分析和端口分析。ICE 一般有一种特殊的 CPU,称为外接 CPU。这是一种封装已被打开的 CPU,通过特殊的连接,可以访问 CPU 内部的信号,而这些信号在封装时是“看不到”的。与工作站上功能强大的调试软件配合使用时,ICE 可以提供您能找到的最全面的调试功能。但 ICE 也有一些缺点:价格昂贵;不能全速工作;而且,并不是所有的 CPU 都可以用作外接 CPU。从另一个角度来看,这些外接 CPU 不太可能及时被新 CPU 替换。虽然 JTAG(联合测试行动组)最初是为了监控 IC 和电路连接而开发的,但这种串行接口已经扩展了它的用途,包括支持调试。 AD为Blackfin设计的Visual Dsp++支持高速JTAG调试。

ROM 监视器

ROM 监视器是一个驻留在嵌入式系统 ROM 中的小程序,它通过串行或网络连接与工作站上运行的调试软件进行通信。这是一种廉价的方式,当然也是最低端的技术。它除了一个通信端口和少量的内存空间外,不需要任何其他特殊硬件。它提供以下功能:下载代码、运行控制、断点、单步执行以及观察和修改寄存器和内存。由于 ROM 监视器是操作软件的一部分,因此它只会在您的应用程序运行时起作用。如果您要检查 CPU 和应用程序的状态,则必须停止应用程序并再次进入 ROM 监视器。

数据监控

该监视器不仅可以在不停止CPU的情况下显示指定变量的内容,还可以采集各个变量的变化过程,并以图形的形式显示出来。

操作系统监视器

操作系统监视器可以显示任务切换、信号量发送和接收、中断等事件。一方面,这些监视器可以向您展示事件之间的相互关系和时间联系;另一方面,它们还可以提供对信号量优先级反转、死锁和中断延迟等问题的诊断。

性能分析工具[Profiler]

它可以用来测试 CPU 的性能。profiler 工具可以让你了解系统瓶颈在哪里、CPU 使用率如何以及需要优化的地方。

8. 内存测试器

你可以发现内存使用方面的问题,比如内存泄漏,内存碎片,内存崩溃等等。如果你发现系统出现了一些不可预测的或者间歇性的问题,那么你应该使用内存测试工具来测试一下。

9. 执行追踪器

可以显示CPU执行了哪些函数,谁调用了这些函数,参数是什么,什么时候调用的等等。该工具主要用于测试代码逻辑,可以在大量的事件中发现异常。

10. 覆盖率测试器

它主要展示哪些代码被CPU执行了,哪些代码分支没有被执行,有助于提高代码质量,剔除无用代码。

11.GUI 测试器

许多嵌入式应用程序都具有某种形式的图形用户界面用于交互,一些系统性能测试基于用户输入响应时间。GUI 测试工具可用作脚本工具,在开发环境中运行测试用例。其功能包括记录和重放操作、捕获屏幕显示以供以后分析和比较以及设置和管理测试流程(Rational 的 robot 和 Mercury 的 Loadrunner 工具是杰出的代表)。许多嵌入式设备没有 GUI,但嵌入式设备通常可以插入以运行 GUI 测试脚本。虽然这种方法可能需要更改被测试的代码,但它为功能测试和回归测试节省了时间。

自制测试仪

在嵌入式应用中,有时候需要自己写一些工具来实现针对某一特定目的的某种测试目的。我编写的视频流录制工具在测试视频会议数据的流动和变化时非常有用,帮助公司发现了几个隐藏很深的bug。

2. 尽早发现记忆问题

内存问题危害很大,而且不容易排查,主要有三种:内存泄漏、内存碎片、内存崩溃。对待内存问题的态度一定要明确,就是早发现早“治疗”。在软件设计中,内存泄漏是最“出名”的,主要是因为不断分配的内存不能及时释放,久而久之,系统的内存就被耗尽了。即使是细心的编程老手,有时也会遇到内存泄漏。测试过内存泄漏的朋友大概都有一个深刻的体会,那就是内存泄漏一般都隐藏得很深,通过阅读代码很难发现。有些内存泄??漏甚至可能出现在库中,可能是库本身的bug,也可能是因为程序员没有正确理解他们的接口描述文档,导致误操作。

很多时候,大多数内存泄漏是无法被察觉的,而可能表现为随机故障。程序员经常把这种现象归咎于硬件问题。如果用户对系统稳定性不是很有信心,重启系统问题不大;但如果用户对系统稳定性很有信心,这种故障可能会导致用户对产品失去信心,同时也意味着你的项目是一个失败的项目。由于内存泄漏的危害巨大,现在有很多工具可以解决这个问题。这些工具通过查找未引用或重用的代码块、垃圾内存收集、库跟踪等技术来发现内存泄漏。每种工具都有其优缺点,但总的来说,用总比不用好。总之,负责任的开发人员应该对内存泄漏进行测试,防患于未然。

内存碎片比内存泄漏更加隐蔽,随着内存的不断分配和释放,大块内存不断被分解成小块内存,从而形成碎片。久而久之,当需要申请大块内存时,可能会失败。如果系统内存足够大,可以撑得更久,但最终还是逃脱不了分配失败的命运。在使用动态分配的系统中,经常会出现内存碎片的情况。目前解决这个问题最有效的办法就是利用工具显示系统中的内存使用情况,找出谁是造成内存碎片的罪魁祸首,然后针对相应的部分进行改进。

由于动态内存管理存在各种问题,在嵌入式应用程序中,许多公司只是禁用 malloc/free 以避免将来出现问题。

内存损坏是内存使用最严重的后果,主要原因包括数组越界访问、写入已释放的内存、指针计算错误、堆栈地址越界访问等。此类内存损坏会随机导致系统故障,且很难发现。目前可用的故障排除工具很少。

总之,如果你想使用内存管理单元,你必须小心谨慎,严格遵守它们的使用规则,比如谁分配,谁释放。

3. 深入理解代码优化

说到系统稳定性,人们往往更多地考虑实时性和速度,因为代码效率对于嵌入式系统来说太重要了。懂得如何优化代码是每个嵌入式软件开发人员必须具备的技能。就像一个减肥的女孩,至少要知道自己最需要减肥的地方在哪里,才能去买减肥药或者减肥器材。可见,代码优化的前提是找到真正需要优化的地方,然后对症下药,优化代码的相应部分。前面提到的profile(性能分析工具,有些功能齐全的IDE提供了这个内置工具)可以记录各个任务的CPU使用率、各个任务的优先级是否分配合理、某个数据被复制了多少次、磁盘被访问了多少次、网络收发程序是否被调用、测试代码是否被关闭等各种情况。

但是,仅使用 Profile 工具来分析实时系统性能还是不够的。一方面,人们经常在系统出现问题,也就是 CPU 耗尽之后才使用 Profile 工具,而 Profile 工具本身就占用了大量的 CPU,因此 Profile 可能不适用于这种情况。根据海森堡效应,任何测试方法都会或多或少地改变系统运行,这对于 Profile 工具来说也同样适用!

总之,提高运行效率的前提是必须知道CPU干了什么以及它是怎样干的。

4. 不要大海捞针

大海捞针只是调试的一个生动的比喻。

经常听到群里有人对着自己正在调试的代码说废话!这是可以理解的,因为代码不是他写的,他有足够的理由对有bug的代码说废话,只要他不写这样的代码,否则有一天同群里的其他人可能也会对他写的代码说废话。为什么要大海捞针?肯定是有人把针掉进海里了;为什么针会掉进海里?肯定是有人粗心大意或者大意了。所以当你抱怨针这么难找的时候,有没有想过是自己不小心把它掉在了地上。同样,当你调试得半死不活的时候,有没有想过反思自己是否可能没有严格遵循良好的编码设计规范,没有检查某些假设或算法的正确性,没有标记某些可能有问题的代码,以寻求捷径?如何写出高质量的代码,可以参考林锐的《高质量C++/C编程指南》或者《0x8个关于C的“真经”》。

如果你确实掉针了,你必须采取一些预防措施,比如戴上安全手套,以防止在找到针之前就被扎伤。同样,为了尽可能地暴露和捕获问题的根本原因,我们可以设计更全面的错误跟踪代码。怎么做呢?尽量处理每个函数调用失败,尽量检查每个参数输入和输出(包括指针)的有效性,检查某个过程是否调用过多或过少。错误跟踪可以让你大致知道你把针掉在哪里了。

5 重现并隔离问题

如果把针扔进大海而不是大海,处理起来会容易得多。因为至少我们可以把大海分成很多块,然后一块一块地去找。对于有独立模块的大型项目,使用隔离方法往往是处理那些隐藏很深的 Bug 的最后手段。如果问题间歇性地发生,我们需要尝试重现它,并记录下整个重现的过程,以便下次使用这些条件重现问题。如果你确定可以使用记录的条件重现问题,那么我们就可以开始隔离问题了。如何隔离?我们可以使用 #ifdef 关闭一些可能与问题无关的代码,将系统最小化到问题仍然可以重现的程度。如果仍然无法定位问题,那么就需要打开“工具箱”。你可以尝试使用 ICE 或数据监视器查看可疑变量的变化;可以使用跟踪工具获取包括参数传递在内的函数调用;检查是否存在内存崩溃和堆栈溢出问题。

6. 以退为进

为了避免在森林中迷路,猎人常常会在树上留下一些标记,以便将来某天迷路了的话,可以根据这些标记找到出去的路。跟踪并记录过去代码的改动对于以后的调试很有帮助。如果有一天,你上次修改的程序在运行了很久之后突然死掉了,那么你此时的第一反应就是我到底改了什么,因为上次修改之前它都好好的。那么如何检测与上次相比的变化呢?没错,就是代码控制系统SCS或者版本控制系统VCS(Concurrent Version Control,CVS是VCS的演化版本)。签入上一个版本,并与当前的测试版本进行比较。比较工具可以是SCS/VCS/CVS自带的diff工具或者其他更强大的比较工具,比如BeyondCompare、ExamDiff等。通过比较,记录下所有修改过的代码,分析出所有可能引发问题的可疑代码。

7. 确定测试的完整性

如何知道你的测试有多全面?覆盖率测试可以回答这个问题。覆盖率测试工具可以告诉你 CPU 实际执行了哪些代码。一个好的覆盖率工具通常可以告诉你大约 20% 到 40% 的代码没有问题,而其余的可能有错误。覆盖率工具有不同的测试级别,用户可以根据需要选择一个级别。即使你非常确定你的单元测试是全面的,没有死代码,覆盖率工具仍然可以为你指出一些潜在的问题。看看下面的代码:

如果 (i >= 0 && (almostAlwaysZero == 0 || (last = i)))

如果almostAlwaysZero非零,则跳过last=i赋值语句,这可能不是你期望的。使用覆盖率工具的条件测试功能可以轻松发现此问题。

总之,覆盖率测试对于提高代码质量非常有帮助。

8.提高代码质量并节省时间

研究表明,软件开发80%以上的时间都花在以下方面:

更糟糕的是,你可能需要花费 10-200 倍以上的时间来寻找一个一开始可能很容易发现的 Bug。一个小小的 Bug 可能会让你付出巨大的代价,即使这个 Bug 对整个系统的性能没有太大的影响,但很可能会影响到你能看到的部分。因此,我们必须制定良好的编码和测试方法,以实现更高的代码质量,缩短调试代码的时间。

9. 发现它、分析它、解决它

这世上没有万能的药膏,再强大的profile也有无能为力的时候;再好的内存监控也有发现不了的时候;再好的覆盖工具也有覆盖不到的地方。有些隐藏很深的问题,可能用尽所有工具都找不到。这时候我们能做的就是通过这些问题的外在现象或者一些数据输出,去寻找规律或者异常。一旦发现任何异常,就要深入理解,追溯其根源,直至解决。

10. 利用初学者的心态

有人曾说过:“有些事情在初学者的头脑中可能很另类,但在专家的头脑中却可能很简单。”有时候,一些简单的问题被想得很复杂,一些简单的系统被设计得很复杂,就是因为你的“专家思维”。当你被一个问题难倒时,关掉电脑,出去走走,和你的朋友甚至你的狗谈论你的问题。也许它们能给你意想不到的灵感。

总结:嵌入式调试也是一门艺术,和其他艺术一样,想要成功,必须有智慧,有经验,懂得使用工具,只要我们能理解好Oracle的十个秘密,相信我们一定能在嵌入式测试中取得成功。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码