在进行产品开发或者软件设计时,有没有遇到过下面的这种情况:
程序本来运行的好好的,莫名其妙的就出现了bug,但是还找不到出现bug的规律?
已经验证好的产品,到客户那里突然出现了问题?
出现的bug总是莫名其妙,找不到规律,感觉像“幽灵”一般的存在?
增加了一行无关的代码,程序就不能运行了?
其实,有的时候,问题原因会以一种微妙的方式影响代码的存在,今天我们重点讲解一下难以发现的、容易导致软件问题的5种情况,以及所对应的应对措施。
01
堆栈溢出
肯定很多程序员都非常了解“堆栈溢出”这种情况。堆栈溢出可能会对数据或者指令造成破坏,从而影响程序的正确运行。 并且,发生堆栈溢出这种错误,在嵌入式设备程序中造成的影响比计算机中更大。通常有以下几种原因:
1、嵌入式系统通常使用较少的RAM
2、通常没有可依赖的虚拟内存(因为没有磁盘)
3、基于RTOS任务的固件设计使用多个堆栈(每个任务一个),每个堆栈的大小必须足够大,以确保不受唯一最坏情况堆栈深度的影响
4、中断处理程序可能会尝试使用这些堆栈 在进行相关测试的时候,有的时候堆栈溢出这种错误我们并不能测试出来,这就造成了:经过测试验证的程序,到客户处突然运行异常了。
为了避免发生堆栈溢出这种错误,我们可以通过自上而下的代码控制流分析方法,来证明代码是否会出现堆栈溢出错误。
避免措施:
1、确定好自己程序定义的堆栈的全部空间地址;
2、在临近堆栈的位置,定义固定地址的数组或者数据;
3、在程序中实时检查数组或者数据的值,若发现数据改变,证明发生了堆栈溢出,增加特殊的处理姿势:如让设备进入到特定的安全模式,或者输出当前的PC地址等。
02
竞争条件
在程序运行过程中,大量的、无序的任务一直在运行,但是资源是有限的,两个不同的任务之间可能就存在竞争资源的情况,由于两个或者多个进程竞争使用不能被同时访问的资源,使得这些进程有可能因为时间上推进的先后原因而出现问题,这叫做竞争条件。
条件竞争就是两个或者多个进程或者线程同时处理一个资源(如全局变量、文件等)产生非预想的执行效果,从而产生程序执行流的改变,从而达到攻击的目的。
防止条件竞争的方法如下:
1、采用某种保护机制来保护数据(如互斥体),确保只有进行修改的线程才能看到不变量被破坏时的中间状态;
2、使用无锁编程;
3、使用事务来处理更新,将数据和读取都存储到事务日志中,然后将之前的操作合并为一步,再进行提交。当数据被另一个线程修改后,或处理已经重启的情况下,提交就会无法进行。
03
不可重入函数
首先需要区分什么是可重入什么是不可重入函数?
可重入函数:可重入函数可以由多个任务并发使用,而不必担心数据错误
不可重入函数:不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量,或者在代码的关键部分禁用中断)
一些常见的不可重入函数的情形:
函数中使用了静态变量,无论是全局变量还是局部静态变量
函数返回静态变量
函数中调用了不可重入函数
函数体内使用了静态的数据结构
函数体内调用了malloc()或者free()函数
函数体内调用了其他标准I/O函数
函数是singleton中的成员函数而且使用了不使用线程独立存储的成员变量
总得来说,如果一个函数在重入条件下使用了未受保护的共享资源,那么他就是不可重入的。
例如两个函数func1和func2都是不可重入函数:对多线程条件下,操作系统会在func1还没有执行完的情况下,切换到另一个线程中,那个线程可能再次调用func1,这样状态就错了。
避免措施:
在每个库或驱动程序模块中创建并隐藏一个本质上不可重入的互斥锁。将此互斥锁的获取作为操作整个模块中使用的任何持久数据或共享寄存器的先决条件。
04
优先级翻转
优先级翻转是当一个高优先级任务通过信号量机制访问共享资源时,该信号量已被一低优先级任务占有,因此造成高优先级任务被许多具有较低优先级任务阻塞,实时性难以得到保证。
比较经典的由于优先级翻转造成的事故就是当年的火星探路者号,就由于,此处所说的,优先级反转,而导致了内部执行逻辑出错的bug。
在1997年7月4号发射后,在开始搜集气象数据之后没几天,系统(无故)重启了。后来,当然,被相关技术人员找到问题根源,就是,这个优先级反转所导致的,然后修复了此bug。
解决措施:
1、优先级天花板
优先级天花板是当线程申请某资源时,把该线程的优先级提升到可访问这个资源的所有线程中的最高优先级,这个优先级称为该资源的优先级天花板。这种方法简单易行,不必进行复杂的判断,不管线程是否阻塞了高优先级线程的运行, 只要线程访问共享资源都会提升线程的优先级。
2、优先级继承
优先级继承是当线程A申请共享资源Source时,如果共享资源Source正在被线程C使用,通过比较线程C与自身的优先级,如发现线程C的优先级小于自身的优先级, 则将线程C的优先级提升到自身的优先级,线程C释放资源Source后,再恢复线程C的原优先级。这种方法只在占有资源的低优先级线程阻塞了高优先级线程时才动态的改变线程的优先级。
05
内存泄漏
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
解决措施: 当申请了动态区域,用完的时候一定要记得释放(free),如果没有释放,那么这块内存区域就将处于不可用状态(就像占着茅坑不拉屎一样),程序大了或运行久了就极有可能会导致内存的泄露(重启一下就能解决90%的问题根源),同时我们在释放的时候也要注意释放的内存只能释放一次,不要重复的释放,有的时候代码量会比较大,所以有可能会在不止一处地方进行了代码的释放操作。因为我们内存释放了一次后,该内存区域就有可能用来做别的事了,如果这时候我们又再释放一遍就很有可能会出现问题了。释放完之后最好把指针指向空地址,避免下次再使用指针的时候出现地址的错误。
审核编辑:刘清
-
RAM
+关注
关注
8文章
1368浏览量
114704 -
中断处理
+关注
关注
0文章
94浏览量
10976 -
RTOS
+关注
关注
22文章
813浏览量
119644 -
堆栈溢出
+关注
关注
0文章
9浏览量
7911
原文标题:不要以为莫名其妙的bug是玄学!介绍5个引起程序隐藏bug的原因以及预防措施。
文章出处:【微信号:精通单片机与嵌入式,微信公众号:精通单片机与嵌入式】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论