前言
最近在開發(fā)調(diào)試基于RT-Thread 的程序時(shí),遇到一個(gè)比較奇怪的死機(jī)問題,后來經(jīng)過一步步排查,終于發(fā)現(xiàn)是動(dòng)態(tài)內(nèi)存申請(qǐng)的數(shù)據(jù)結(jié)構(gòu)沒有清零引發(fā)的死機(jī)。
排查方法
由于沒有單步調(diào)試的手段,就通過 打印調(diào)試LOG 與 #if 0 A_CODE #else B_CODE #endif 條件編譯的方式,通過注釋部分代碼等方法,快速縮小問題的排查范圍。
最終逐步排查,定位在內(nèi)存資源釋放的函數(shù)部分 xxfree,也就是程序執(zhí)行完了,釋放動(dòng)態(tài)申請(qǐng)的內(nèi)存時(shí),觸發(fā)了死機(jī),注釋掉這部分代碼,不死機(jī)了,不過這樣會(huì)造成【內(nèi)存泄露】
通過查看代碼繼續(xù)排查,xxfree 函數(shù)設(shè)計(jì)的沒有問題,所有指針都有判空操作,也就是只會(huì) free 非空的指針,但是這種必現(xiàn)的死機(jī),就幾段內(nèi)存 free 的代碼,排查應(yīng)該容易,所以增加了些LOG,并且把 free 時(shí)的指針地址也打印出來,確認(rèn)是否 free 了不屬于自己的指針或者重復(fù) free 指針。
問題定位
在 xxfree 函數(shù) 增加多行 LOG后,再次運(yùn)行死機(jī)后,我看了一下死機(jī)后的 LOG 信息,瞬間找到了方向,原來 free 的指針不是NULL,而是 0xffffffff,怪不得會(huì)死機(jī)!
通過全局搜索這個(gè)指針成員,竟沒有發(fā)現(xiàn)賦值的地方,也就是沒有賦值,希望它為 NULL,而它實(shí)際上是 0xffffffff。
這個(gè)指針附屬于一個(gè)大的結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體是通過 malloc 方式動(dòng)態(tài)內(nèi)存申請(qǐng)的,但是沒有【清零】操作,并且后續(xù)的操作中,這個(gè)指針成員由于某種條件下,沒有代碼執(zhí)行到賦值,默認(rèn)【野指針】。并且這個(gè)板子動(dòng)態(tài)申請(qǐng)的內(nèi)存默認(rèn)不是0x00,而是 0xff,所以 free 了 非空的野指針觸發(fā)死機(jī)
解決方法:malloc 后的數(shù)據(jù)結(jié)構(gòu),增加 memset 清零操作,這樣保證指針成員默認(rèn)為 NULL,如果是野指針, 并且free 時(shí)不為 NULL,會(huì)造成釋放內(nèi)存操作異常,大概率觸發(fā)死機(jī)。
觸累與排雷
繼續(xù)排查驅(qū)動(dòng)里面,有幾處也是通過動(dòng)態(tài)內(nèi)存申請(qǐng) malloc 數(shù)據(jù)結(jié)構(gòu) 后,也沒有 memset 清零,并且里面有 鏈表成員,鏈表也沒有初始化,并且【僥幸成功】的掛上了 鏈表頭 head 上,沒有觸發(fā)死機(jī)。
由于 資源釋放這個(gè)操作一般都發(fā)生在【游戲結(jié)束】后,也就是代碼不運(yùn)行了,所以沒有特別的注意,即使鏈表的成員沒有清零操作,指針為野指針,也能 插入到 鏈表頭,但是當(dāng)多次這種操作后,就會(huì)觸發(fā)內(nèi)存異常,因?yàn)闆]有【清零】的內(nèi)存空間,數(shù)據(jù)可能不是零,在地雷的旁邊走,就有踩上去的機(jī)會(huì)!
加了 memset 清零操作后,果然觸發(fā)了幾個(gè)死機(jī),并且牽出來 【鏈表未初始化】 就直接使用的 BUG 操作。
有些操作可能運(yùn)行一次并未發(fā)生死機(jī)等【當(dāng)場(chǎng)就出現(xiàn)】的死機(jī)等錯(cuò)誤,但是多次運(yùn)行,錯(cuò)誤累積起來,就會(huì)觸發(fā)更嚴(yán)重的錯(cuò)誤
代碼編寫時(shí),規(guī)范化、充分測(cè)試非常有必要。
調(diào)試改善代碼的時(shí)間往往比編寫代碼時(shí)間多很多,因此在代碼設(shè)計(jì)編寫時(shí)要多花點(diǎn)心思與時(shí)間,充分驗(yàn)證,減少后續(xù)問題的排查調(diào)試時(shí)間,提高工作效率。
小結(jié)
復(fù)雜點(diǎn)的數(shù)據(jù)結(jié)構(gòu),使用 動(dòng)態(tài)內(nèi)存申請(qǐng)后,建議直接 清零操作,以免默認(rèn)的數(shù)據(jù)不是自己想要的,造成程序運(yùn)行異常或者死機(jī),簡單的數(shù)據(jù)結(jié)構(gòu),如果能保證后面的操作把每個(gè)成員都賦值上,可以不清零。
初始化、清零,這些簡單的操作,也需要重視起來,以免耗費(fèi)大量的軟件排查與調(diào)試時(shí)間。
-
動(dòng)態(tài)內(nèi)存
+關(guān)注
關(guān)注
1文章
24瀏覽量
7977 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1290瀏覽量
40154
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論