电子说
前言
最近在开发调试基于RT-Thread 的程序时,遇到一个比较奇怪的死机问题,后来经过一步步排查,终于发现是动态内存申请的数据结构没有清零引发的死机。
排查方法
由于没有单步调试的手段,就通过 打印调试LOG 与 #if 0 A_CODE #else B_CODE #endif 条件编译的方式,通过注释部分代码等方法,快速缩小问题的排查范围。
最终逐步排查,定位在内存资源释放的函数部分 xxfree,也就是程序执行完了,释放动态申请的内存时,触发了死机,注释掉这部分代码,不死机了,不过这样会造成【内存泄露】
通过查看代码继续排查,xxfree 函数设计的没有问题,所有指针都有判空操作,也就是只会 free 非空的指针,但是这种必现的死机,就几段内存 free 的代码,排查应该容易,所以增加了些LOG,并且把 free 时的指针地址也打印出来,确认是否 free 了不属于自己的指针或者重复 free 指针。
问题定位
在 xxfree 函数 增加多行 LOG后,再次运行死机后,我看了一下死机后的 LOG 信息,瞬间找到了方向,原来 free 的指针不是NULL,而是 0xffffffff,怪不得会死机!
通过全局搜索这个指针成员,竟没有发现赋值的地方,也就是没有赋值,希望它为 NULL,而它实际上是 0xffffffff。
这个指针附属于一个大的结构体,这个结构体是通过 malloc 方式动态内存申请的,但是没有【清零】操作,并且后续的操作中,这个指针成员由于某种条件下,没有代码执行到赋值,默认【野指针】。并且这个板子动态申请的内存默认不是0x00,而是 0xff,所以 free 了 非空的野指针触发死机
解决方法:malloc 后的数据结构,增加 memset 清零操作,这样保证指针成员默认为 NULL,如果是野指针, 并且free 时不为 NULL,会造成释放内存操作异常,大概率触发死机。
触累与排雷
继续排查驱动里面,有几处也是通过动态内存申请 malloc 数据结构 后,也没有 memset 清零,并且里面有 链表成员,链表也没有初始化,并且【侥幸成功】的挂上了 链表头 head 上,没有触发死机。
由于 资源释放这个操作一般都发生在【游戏结束】后,也就是代码不运行了,所以没有特别的注意,即使链表的成员没有清零操作,指针为野指针,也能 插入到 链表头,但是当多次这种操作后,就会触发内存异常,因为没有【清零】的内存空间,数据可能不是零,在地雷的旁边走,就有踩上去的机会!
加了 memset 清零操作后,果然触发了几个死机,并且牵出来 【链表未初始化】 就直接使用的 BUG 操作。
有些操作可能运行一次并未发生死机等【当场就出现】的死机等错误,但是多次运行,错误累积起来,就会触发更严重的错误
代码编写时,规范化、充分测试非常有必要。
调试改善代码的时间往往比编写代码时间多很多,因此在代码设计编写时要多花点心思与时间,充分验证,减少后续问题的排查调试时间,提高工作效率。
小结
复杂点的数据结构,使用 动态内存申请后,建议直接 清零操作,以免默认的数据不是自己想要的,造成程序运行异常或者死机,简单的数据结构,如果能保证后面的操作把每个成员都赋值上,可以不清零。
初始化、清零,这些简单的操作,也需要重视起来,以免耗费大量的软件排查与调试时间。
全部0条评论
快来发表一下你的评论吧 !