星標公眾號,讓嵌入式知識 “投喂” 不停歇!
在嵌入式開發(fā)中,最令人頭疼的莫過于內(nèi)存踩踏問題。它們像幽靈一樣時隱時現(xiàn),讓系統(tǒng)在測試時正常運行,卻在客戶現(xiàn)場莫名崩潰。
這次我們來介紹一個高效定位內(nèi)存踩踏問題的利器——mprotect。
mprotect是Linux/Unix系統(tǒng)調(diào)用,通過操縱MMU(內(nèi)存管理單元)的頁表權限,實現(xiàn)對指定內(nèi)存區(qū)域的保護。當發(fā)生非法訪問時,系統(tǒng)立即拋出SIGSEGV信號,讓我們能夠精確定位問題發(fā)生的第一現(xiàn)場。
mprotect()函數(shù)的原型如下:
#include <unistd.h>
#include <sys/mmap.h>
int mprotect(const void *start, size_t len, int prot);
mprotect()函數(shù)把自start開始的、長度為len的內(nèi)存區(qū)的保護屬性修改為prot指定的值。
prot可以取以下幾個值,并且可以用“|”將幾個屬性合起來使用:
PROT_READ:表示內(nèi)存段內(nèi)的內(nèi)容可讀;
PROT_WRITE:表示內(nèi)存段內(nèi)的內(nèi)容可寫;
PROT_EXEC:表示內(nèi)存段中的內(nèi)容可執(zhí)行;
PROT_NONE:表示內(nèi)存段中的內(nèi)容根本沒法訪問。
與valgrind等工具相比,mprotect有三大優(yōu)勢:
mprotect的局限性:
相關文章:
分享一個嵌入式開發(fā)調(diào)試利器!
嵌入式系統(tǒng)內(nèi)存泄漏檢測利器:MTrace
工具 | Valgrind仿真調(diào)試工具的使用
mprotect本質(zhì)上是Linux內(nèi)核提供的內(nèi)存頁保護機制。它的工作原理可以用一個簡單的比喻來理解:
想象你的內(nèi)存是一棟大樓,mprotect就是給每個房間安裝不同級別的門禁系統(tǒng)。當有人試圖違規(guī)進入時,門禁立即報警并通知保安(信號處理函數(shù))。
權限檢查流程:

讓我們深入分析一個完整的mprotect內(nèi)存保護實現(xiàn):

說明:
siginfo_t結(jié)構(gòu)體的si_addr字段精確記錄了違規(guī)地址
內(nèi)存布局圖:

說明:

說明:
頁面地址對齊計算 addr & ~(PAGE_SIZE - 1) 是一個位運算技巧,相當于 (addr / PAGE_SIZE) * PAGE_SIZE,但效率更高。




本文完整代碼例子可在嵌入式大雜燴公眾號聊天界面輸入關鍵字獲取:mprotect_demo
mprotect不是萬能的,它有明顯的局限性——保護粒度較粗(頁級別)、頻繁切換有性能開銷。但在定位詭異的內(nèi)存踩踏問題時,它提供的實時精確打擊能力是其他工具難以替代的。
另外,我們之前也有分享過類似的內(nèi)存檢測的思路:
我們在申請內(nèi)存時,在申請內(nèi)存的前后增加兩塊標識區(qū)(紅區(qū)),里面寫入固定數(shù)據(jù)。申請、釋放內(nèi)存的時候去檢測這兩塊標識區(qū)有沒有被破壞(檢測操作堆內(nèi)存時是否踩到高壓紅區(qū))。


感興趣的小伙伴可閱讀:談談嵌入式 C 語言踩內(nèi)存問題!