程序的编写时,可能经常会遇到程序崩溃的现象。一般来说,程序崩溃是由于内存操作不当引发的。但是具体来讲,由哪些原因可以导致程序崩溃呢?以及当程序崩溃时该如何找到错误的位置呢?
1.资源不足:当应用程序需要使用的资源(如内存、存储空间等)超过了系统可用的资源时,就容易导致程序崩溃。
2.系统错误:应用程序运行的环境是操作系统,如果操作系统出现错误,就容易导致应用程序崩溃。
3.第三方库或插件问题:应用程序可能会依赖一些第三方库或插件,如果这些库或插件出现问题,就可能会导致应用程序崩溃。
4.不确定的变量:程序使用了随机的数字或变量组件,在程序执行时没有被很好的确定范围。如:用户输入,随机数,或时间。这种往往是疏忽大意造成的,因为逻辑错误非常明显。
5.内存泄漏:内存泄露是指程序在申请内存后,无法释放或未能及时释放,从而导致系统内存的不断消耗,最终导致程序的崩溃或性能的下降。这种现象一般发生在程序中有大量的动态内存分配和释放操作,如果程序员忘记或者疏忽了释放内存,就有可能导致内存泄露。
程序每次执行时都内存溢出。每次运行程序崩溃的罪魁祸首是随机的,因为它依赖于在特定时间运行的进程数。包括堆溢出或者是栈异常。
内存泄露的原因:
(1. 引用计数法:当引用计数法用于管理动态内存时,可能会出现引用计数出错的情况,从而导致内存泄露。
(2. 资源泄漏:由于程序设计中缺乏对资源的有效管理,对资源的申请和释放没有做到对称,也可能导致内存泄露。
(3. 循环引用:当多个对象彼此引用而产生循环引用,而这些对象中至少有一个是动态分配的,这样就会出现内存泄漏的情况。
解决办法:
(1. 避免使用引用计数法来管理动态内存,而是使用其他的内存管理方法,如垃圾回收等。
(2. 合理利用RAII机制,在编程中能够有效地管理资源。
(3. 避免产生循环引用的情况,如果产生了循环引用,应该尽量采用弱引用的方式来解决。
以下两种情况会导致函数栈溢出:
(1)定义了一个体积太大的局部变量。当变量体积太大时,应该用malloc或new来动态分配内存。
(2)函数嵌套调用,层次过深(如无穷递归),没有终止条件。
探讨内存泄漏:定义、原因、检测和避免方法以及影响 (baidu.com)
6.设备兼容性问题:不同的设备具有不同的硬件和软件配置,如果APP没有考虑到设备的兼容性问题,就会导致APP在某些设备上崩溃。
7.数组越界访问:当程序尝试访问超出数组内存范围的地址时,就会发生内存访问错误。这种错误会导致程序崩溃,因为操作系统会检测到程序尝试访问一个无效的内存地址,并强制关闭该程序。
除了导致程序崩溃外,数组越界还可能会导致其他严重的问题,例如内存泄漏、数据损坏等。因此,在编写程序时,一定要注意数组越界的问题,并尽可能避免出现这种情况。
8.指针的目标对象不可用:(1)空指针(2)野指针
9.程序复位 软件问题:看门狗复位。硬件问题:供电电压不稳、电源带载能力不足
处理方法
在嵌入式系统中,程序错误可能会导致系统的崩溃或不可预测的行为。因此,对于嵌入式系统中的程序错误,需要采取一些特殊的处理方法来确保系统的可靠性和稳定性。
1. 异常处理
异常处理是一种用于处理程序错误的机制,可以在程序运行时捕获错误并采取适当的措施来避免系统崩溃或导致系统不可用。在嵌入式系统中,异常处理通常由操作系统或硬件提供支持。当程序错误发生时,操作系统或硬件会捕获异常并进行相应的处理,例如重启系统或记录错误信息以供后续分析和修复。
2. 调试和测试
调试和测试是处理嵌入式系统中程序错误的另一种重要手段。通过使用调试器和测试工具,可以检测和诊断程序错误,并进行修复。在嵌入式系统中,调试和测试通常需要使用专门的硬件和软件工具,例如仿真器、调试器、性能分析器等。
3. 容错设计
容错设计是一种用于处理嵌入式系统中程序错误的重要技术。通过设计容错机制,可以在程序错误发生时自动纠正错误或采取其他措施来保证系统的可靠性和稳定性。例如,在嵌入式系统中,可以使用冗余设计来提高系统的可靠性,例如使用双重或三重冗余技术来保证系统在出现错误时仍能正常工作。
4. 硬件保护
硬件保护是一种在嵌入式系统中处理程序错误的重要技术。通过使用硬件保护机制,可以在程序错误发生时自动隔离错误,避免错误对系统的影响。例如,在嵌入式系统中,可以使用硬件中断来隔离程序错误,保护系统不受错误的影响。
5. 日志记录
日志记录是一种处理嵌入式系统中程序错误的重要手段。通过记录系统运行时的信息和错误信息,可以帮助开发人员及时发现和解决程序错误。在嵌入式系统中,可以使用日志记录机制来记录系统运行时的信息和错误信息,并将其保存在文件或存储器中,以供后续分析和修复。
总之,处理嵌入式系统中程序错误需要采取一系列的技术和手段,包括异常处理、调试和测试、容错设计、硬件保护和日志记录等。这些技术和手段可以帮助嵌入式系统开发人员及时发现和解决程序错误,提高系统的可靠性和稳定性。
一、问题复现
稳定复现问题才能正确的对问题进行定位、解决以及验证。一般来说,越容易复现的问题越容易解决。
模拟复现条件
有的问题存在于特定的条件下,只需要模拟出现问题的条件即可复现。对于依赖外部输入的条件,如果条件比较复杂难以模拟可以考虑程序里预设直接进入对应状态。
提高相关任务执行频率
例如某个任务长时间运行才出现异常则可以提高该任务的执行频率。
增大测试样本量
程序长时间运行后出现异常,问题难以复现,可以搭建测试环境多套设备同时进行测试。
二、问题定位
缩小排查范围,确认引入问题的任务、函数、语句。
打印LOG
根据问题的现象,在抱有疑问的代码处增加LOG输出,以此来追踪程序执行流程以及关键变量的值,观察是否与预期相符。
在线调试
在线调试可以起到和打印LOG类似的作用,另外此方法特别适合排查程序崩溃类的BUG,当程序陷入异常中断(HardFault,看门狗中断等)的时候可以直接STOP查看call stack以及内核寄存器的值,快速定位问题点。
版本回退
使用版本管理工具时可以通过不断回退版本并测试验证来定位首次引入该问题的版本,之后可以围绕该版本增改的代码进行排查。
二分注释
二分注释即以类似二分查找法的方式注释掉部分代码,以此判断问题是否由注释掉的这部分代码引起。
具体方法为将与问题不相干的部分代码注释掉一半,看问题是否解决,未解决则注释另一半,如果解决则继续将注释范围缩小一半,以此类推逐渐缩小问题的范围。
三、问题分析处理
结合问题现象以及定位的问题代码位置分析造成问题的原因。
四、回归测试
问题解决后需要进行回归测试,一方面确认问题是否不再复现,另一方面要确认修改不会引入其他问题。
五、经验总结
总结本次问题产生的原因及解决问题的方法,思考类似问题今后如何防范,对相同平台产品是否值得借鉴,做到举一反三,从失败中吸取经验。
参考:
嵌入式中程序错误如何处理? - 知乎 (zhihu.com)
嵌入式开发常见问题的解决方法-电子发烧友网 (elecfans.com)