《 嵌入式系统设计与实践》一一3.7 处理错误

简介: 本节书摘来自华章出版社《 嵌入式系统设计与实践 》一 书中的第3章,第3. 7节,作者:Elecia White 著 ,更多章节内容可以访问云栖社区“华章计算机”公众号查看

3.7 处理错误
代码的生命周期让我震惊。因为有时侯好像我们总在以不同的方式重写同样的旧代码,有一天我们可能会发现自己在10年前入门阶段写的一段代码正在被一个财富500强公司使用。既然代码工作的如此之好,为什么还要修复其中那些隐藏很深的问题呢?
在某些时候,代码将失效。这一点可能让人会感到可怕。一个错误的发生,要么是由于代码本身,要么是由于环境中一个意想不到的情况。有两种方法来处理错误。首先,该系统可以进入优雅降级的状态,在这个状态中软件会尽可能做到最好。或者,系统可能会优雅地立即失效。长期运行的传感器类型的系统需要采用前一个方法,医疗系统则要求后者。无论哪种方式,系统必须安全地失效。
但如何实现其中之一的方法?更重要的是,应该采用什么标准来确定哪些子系统应该实现哪一种错误处理方法?具体怎么做取决于不同的产品要求,我们需要在设计期间思考错误处理问题。
3.7.1 一致的方法
函数应竭尽所能地处理错误。例如,如果一个变量可能超出范围,那么这个范围应当固定,并适当记录错误。函数可以返回错误以允许调用者处理这些问题。调用者应该检查并处理错误,这意味着可能进一步将其向上传递到一个多层的应用程序。在许多情况下,如果错误不是那么重要,就不需要对其进行检查,那么同样不需要将它返回。另一方面,在有些情况下,只需要返回一个仅用于测试的诊断代码。可以在注释中指出返回的错误代码不是给正常运行的程序使用的,或只用于在ASSERT()函数调用中。
并不是所有的嵌入式系统都实现了ASSERT()函数,但以适合于系统的方法去实现它并不难。可以输出到调试控制台的消息、输出到系统控制台或者日志的消息,一个断点指令(如BKPT),或者甚至在错误发生时触发一个输入/输出线或LED。输出会改变嵌入式系统的时序,因此将错误通信函数分离出来以允许用其他输出方法(如LED),这样做往往是有益的。
一个应用程序或者系统的错误返回代码应该在代码库上进行标准化。可以创建一个高层errorCodes.h文件(或类似的),以枚举的格式提供一致的错误代码定义。建议的错误代码包括:
没有错误(应该总是为0)。
未知的错误(或无法识别的错误)。
错误的参数。
错误的索引(指针超出范围或者为空)。
未初始化的变量或子系统。
灾难性的失效(这可能会导致处理器复位,除非它在开发模式下,在这种情况下,它可能会引发一个断点或自旋循环)。
应该有一个最小数量的错误(一般性错误),这样应用程序可以解释它们。虽然在将特异性丢弃(UART_FAILED_TO_INIT_BECAUSE_SECOND_PARAMETER_WAS_TOO_HIGH)的同时,泛化使得错误处理和使用更容易(如果PARMETER_BAD错误发生在某个子系统中,那么就已经有了一个合适的地方可以开始寻找这个错误)。从本质上讲,让它保持尽可能简单,确保将重要的信息(某个错误)提供给开发者,这样调试时就可以进一步挖掘错误发生的地方和原因。
3.7.2 错误处理库
错误处理库也是一个不错的想法。实现它的方式之一就是让每个函数返回一个错误代码。不用再像下面这样调用函数和检查结果:
error = FunctionFoo();
if (error != NO_ERROR) {
ErrorSet (&globalErrorCode, error);
}
调用错误检查函数中的函数:
ErrorSet(&globalErrorCode, FunctionFoo());
如果这个函数没有返回任何错误,那么ErrorSet函数不会覆盖以前的错误条件。允许一次调用多个函数,并在最后检查错误,而不是在每个函数调用之后都去检查。
在这样的错误处理库中,将有四个函数这四个函数对应用程序有意义:ErrorSet、ErrorGet、ErrorPrint和ErrorClear。这个库应该设计得便于调试和测试,虽然这个机制即使在开发结束后也应当保留在程序里。比如,ErrorPrint可能会从向串口写日志信息转变为只是触发输入/输出线的一个小函数。这不最终用户需要处理的错误,这个是当产品单元不能正常工作时,开发者应当处理的错误。
3.7.3 调试时序错误
在调试硬件/软件之间的交互行为(或者任何时间关键的软件)时,日志或者printf之类的串行输出会改变代码的时序。在大多数情况下,一个与时序相关的问题是否出现(或者消失)取决于输出语句以及(或者)断点的位置。而当工具和问题相互作用时,调试变得尤其困难。
使用错误处理库(或者如第2章所述的日志库)的好处之一,就是我们可以不用实际输出数据,而将数据存储在RAM中,这是一个很快的方法。事实上,如果遇到时序上的麻烦,考虑使用一个小的缓冲区(4~16字节,取决于可用RAM的大小)来保存来自软件的信号(1~2字节)。在代码中,在感兴趣的触发点(例如,ASSERT或者ErrorSet)填充这个缓冲区。当代码退出时间关键区域之后,再将缓冲区的内容转储出来。如果需要对最新发生的错误信息进行处理,则可以使用一个环形缓冲区持续不断地捕获最近发生的一些事件(在第6章中讨论环形缓冲区)。
或者,如果在设计电路板的时候有输入,我强烈推荐在电路板上预留多余的处理器I/O引脚,并可以容易地通过头文件进行访问。它们可以在调试的时候派上用场(特别是复杂的时序问题,如串行输出破坏时序),用于显示被测试的系统状态,并获取处理器周期剖面。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
22天前
|
程序员 测试技术 数据库连接
PHP编程中的错误处理技巧
在PHP编程中,处理错误是至关重要的一环。本文将介绍几种常见的PHP错误类型及其处理技巧,包括语法错误、运行时错误和逻辑错误。通过学习正确的错误处理方法,可以提高代码的可靠性和可维护性,从而更好地保障程序的稳定性和安全性。
17 2
|
5月前
|
前端开发 JavaScript
常见的8个前端防御性编程方案
常见的8个前端防御性编程方案
73 0
一文读懂Can总线错误处理
一文读懂Can总线错误处理
一文读懂Can总线错误处理
|
1月前
|
设计模式 安全 编译器
【C++ 异常】C++异常处理:掌握高效、健壮代码的秘密武器
【C++ 异常】C++异常处理:掌握高效、健壮代码的秘密武器
54 1
|
1月前
|
存储 编译器 C++
嵌入式中C++ 编程习惯与编程要点分析
嵌入式中C++ 编程习惯与编程要点分析
18 1
|
1月前
|
算法 小程序 Linux
如何写出高效清晰的嵌入式C程序
如何写出高效清晰的嵌入式C程序
10 0
|
1月前
|
算法 小程序 Linux
如何编写高效清晰的嵌入式C程序
如何编写高效清晰的嵌入式C程序
8 0
|
存储 芯片
单片机原理与应用以及C51编程技术——硬件体系结构梳理(下)
单片机原理与应用以及C51编程技术——硬件体系结构梳理(下)
293 0
单片机原理与应用以及C51编程技术——硬件体系结构梳理(下)
|
存储 算法 内存技术
单片机原理与应用以及C51编程技术——硬件体系结构梳理(上)
单片机原理与应用以及C51编程技术——硬件体系结构梳理(上)
518 0
单片机原理与应用以及C51编程技术——硬件体系结构梳理(上)
|
芯片 开发者 ice
3.2基础概念:硬件调试器概念及配置方法|学习笔记
快速学习3.2基础概念:硬件调试器概念及配置方法
244 0
3.2基础概念:硬件调试器概念及配置方法|学习笔记

热门文章

最新文章