前言
最近在開發調試基于RT-Thread 的驅動時,遇到一個比較奇怪的死機問題,后來經過一步步排查,終于發現是驅動的鏈表節點沒有初始化造成的死機
問題分析
RT-Thread 的驅動開發完成后,通過編寫串口 shell 測試命令,運行命令后,觸發死機
由于當前缺少單步的調試方法,只能通過增加LOG與打開關閉部分軟件功能,一步步縮小范圍
在函數調用的入口,把某些關鍵的函數調用分別注釋掉驗證,這樣逐步驗證下來,最終縮小到一個函數,調用這個函數就觸發死機。
用到的軟件調試方法
(1)增加LOG,確認代碼能執行到哪些函數,能執行到哪些行
(2)通過 #if 0 A_CODE #else B_CODE #endif 條件編譯的調試方法,大塊注釋部分代碼,確認代碼執行的路徑,縮小排查方向,確認是執行到哪個函數或模塊造成死機的
(3)通過對比代碼來確認問題,比如軟件正常工作過,后來改動死機了,大概率說明是改動造成的,所以可以通過 git BCompare.exe 等代碼管理與比對工具,代碼回溯,兩份新舊代碼對比分析,逐步把排查范圍縮小,從代碼層面分析可能造成死機的原因
問題分析
軟件調試有時候比較的簡單,有時候會比較的復雜,由于這個驅動移植來自其他系統的,數據結構里面的成員比較的多,所以初步通過代碼對比工具如 BCompare 進行代碼對比,發現了一點端倪:由于RT-Thread 暫時不支持 hash list(哈希鏈表),我把 hash list的功能實現 改為了 RT-Thread 的 list 替代,struct rt_list_node。
對比了軟件的其他改動點,雖然改動部分較大,但軟件工作流程差不多,初步排查代碼沒有實質性的差異
通過進一步的排查并縮小范圍,終于發現了問題點:這個函數在 插入鏈表 的操作部分死機了!
通過代碼繼續網上找,發現這個包含 RT-Thread list 的數據節點,是通過 rt_malloc 申請的,并且沒有看到成員 list 使用 rt_list_init 初始化鏈表的操作
所以馬上確認了問題: 鏈表的節點沒有初始化造成的,通過增加 list 初始化,本以為立即解決了問題,但是竟然依舊死機!
意外的BUG發現:數據節點的鏈表的頭,也就是 鏈表 head 也沒有初始化,解決方法同上,需要初始化 鏈表的頭:使用 rt_list_init,這樣問題得到解決
移植的代碼之前使用的 hashlist,聲明時即初始化了,不需要顯示的初始化,而RT-Thread list,必須初始化,否則把鏈表節點插入 鏈表頭部的時候,就會出現 野指針或空指針 訪問成員的問題,肯定會出問題。
解決方法就是 增加鏈表初始化操作
問題回顧
由于先前移植的樣板驅動使用的是 hash list,造成移植后沒有初始化數據結構的鏈表節點,觸發了死機。所以驅動移植時,遇到鏈表時,一定要注意 鏈表頭與鏈表節點的 鏈表初始化問題
另一個注意點:操作空指針的成員,異常信息里面,可能會提示 異常出在一個 較小的 內存地址上。所以遇到死機,并且發現死機的 內存地址很小,可以往 空指針方向排查
RT-Thread 雙向循環鏈表的操作,由于使用的是【宏定義】,也就是鏈表操作函數本身沒有判空的操作,用戶需要有鏈表指針判空的操作。
訪問一個空指針的結構體成員,肯定會觸發內存異常死機。 如 buffer->list 中的 list 為 RT_NULL,那么訪問 buffer->list->next 時候,list 中的 next 成員地址就是非法的內存地址(小地址),就會出現異常死機
小結
鏈表操作需要謹慎,不只是要把 鏈表頭 申請為 全局的,而且每個鏈表的節點,都是需要全局的。
注意鏈表節點會嵌入到一個復雜的數據結構里面,并且使用動態內存申請的方式 創建,這是一定要注意不要漏下 鏈表成員的初始化。
-
驅動器
+關注
關注
52文章
8241瀏覽量
146399 -
Shell
+關注
關注
1文章
365瀏覽量
23384 -
RT-Thread
+關注
關注
31文章
1290瀏覽量
40153
發布評論請先 登錄
相關推薦
評論