在實(shí)時(shí)系統(tǒng)中管理內(nèi)存是一項(xiàng)挑戰(zhàn)。有許多方面需要考慮,例如代碼空間內(nèi)存管理、RAM內(nèi)存管理、內(nèi)存優(yōu)化以及它們?nèi)绾斡绊懶阅艿鹊取O旅媸瞧邆€(gè)通用的技巧,可以幫助嵌入式開(kāi)發(fā)人員開(kāi)始管理他們的內(nèi)存。
避免malloc
在需要確定性計(jì)時(shí)的實(shí)時(shí)系統(tǒng)中,使用malloc動(dòng)態(tài)分配內(nèi)存是一個(gè)壞主意。首先,典型的malloc實(shí)現(xiàn)是不確定的,這意味著即使能夠分配內(nèi)存,也不能保證分配內(nèi)存需要多長(zhǎng)時(shí)間。使用malloc會(huì)產(chǎn)生許多實(shí)時(shí)問(wèn)題,例如
堆碎片
分配內(nèi)存失敗
不確定的行為
不要試探命運(yùn),避開(kāi)malloc就好。
監(jiān)控內(nèi)存映射文件
跟蹤ROM和RAM去向的一個(gè)好方法是查看編譯器生成的內(nèi)存映射文件。這個(gè)文件將告訴開(kāi)發(fā)人員函數(shù)的代碼大小,以及為它們的變量分配了多少內(nèi)存。不同工具的地圖文件通常略有不同,因此需要開(kāi)發(fā)人員打開(kāi)文件并手動(dòng)瀏覽它們,以確定它們的內(nèi)存使用情況。開(kāi)發(fā)人員可以編寫(xiě)一個(gè)Python腳本來(lái)讀取文件,并提供機(jī)制來(lái)查看哪里的優(yōu)化和代碼返工是最有效的。
使用內(nèi)存塊池進(jìn)行動(dòng)態(tài)內(nèi)存分配
有時(shí)候,嵌入式開(kāi)發(fā)人員無(wú)法擺脫靜態(tài)分配所有內(nèi)存的問(wèn)題。應(yīng)用程序可能無(wú)法提前知道需要多少內(nèi)存,或者預(yù)先分配所有內(nèi)存可能需要比微控制器上可用內(nèi)存更多的RAM。不想使用內(nèi)存分配或字節(jié)池的話,開(kāi)發(fā)人員應(yīng)該怎么做呢?答案是使用塊內(nèi)存池。塊內(nèi)存池在固定的內(nèi)存塊中分配內(nèi)存,不像字節(jié)內(nèi)存池一次分配一個(gè)字節(jié)。塊內(nèi)存池的算法是確定性的和快速的!因此,如果你需要?jiǎng)討B(tài)分配內(nèi)存,請(qǐng)使用塊內(nèi)存池。(大多數(shù)實(shí)時(shí)操作系統(tǒng)都有)。
僅將內(nèi)存字節(jié)池用于任務(wù)堆棧分配
RTOS通常包含許多供開(kāi)發(fā)人員分配內(nèi)存的機(jī)制。選項(xiàng)通常是字節(jié)和塊內(nèi)存池。字節(jié)內(nèi)存池的行為與堆非常相似,并像malloc一樣分配內(nèi)存。有一些實(shí)現(xiàn)是確定性的,但是仍然存在堆碎片的潛在問(wèn)題。出于這些原因,強(qiáng)烈建議開(kāi)發(fā)人員在應(yīng)用程序開(kāi)始時(shí)只使用字節(jié)池來(lái)分配內(nèi)存,例如緩沖區(qū)或任務(wù)堆棧。
靜態(tài)分配內(nèi)存
靜態(tài)分配內(nèi)存意味著所有的內(nèi)存分配都是在編譯時(shí)而不是運(yùn)行時(shí)執(zhí)行的。這是確保確定性的最安全的方法,并且不會(huì)有內(nèi)存碎片問(wèn)題。當(dāng)開(kāi)發(fā)人員不能在編譯時(shí)分配內(nèi)存時(shí),一些動(dòng)態(tài)分配任務(wù)控制塊的RTOS就是這種情況,嘗試在系統(tǒng)初始化期間執(zhí)行所有的動(dòng)態(tài)內(nèi)存分配。在啟動(dòng)時(shí)分配內(nèi)存看起來(lái)像是靜態(tài)分配的。
盡量減少RTOS對(duì)象的使用
通過(guò)RTOS創(chuàng)建的每個(gè)對(duì)象,比如任務(wù)、信號(hào)量、消息隊(duì)列等等,都有一個(gè)與之相關(guān)聯(lián)的控制塊??刂茐K本質(zhì)上是一種結(jié)構(gòu),它保存了對(duì)象執(zhí)行其功能所必需的各種參數(shù)。在資源受限的環(huán)境中工作的嵌入式開(kāi)發(fā)人員會(huì)希望盡量減少他們?cè)趹?yīng)用程序中使用的對(duì)象數(shù)量。如果開(kāi)發(fā)人員不密切監(jiān)控RTOS對(duì)象的代碼,它們會(huì)很快開(kāi)始使用大量的RAM。
更改編譯器的默認(rèn)優(yōu)化設(shè)置
在運(yùn)行時(shí)處理內(nèi)存并不是開(kāi)發(fā)人員會(huì)遇到的唯一內(nèi)存管理問(wèn)題。有時(shí),開(kāi)發(fā)人員需要嘗試優(yōu)化RAM和ROM,以便最大限度地降低他們使用的微控制器的BOM成本。在許多情況下,編譯器(如GCC)默認(rèn)不包含最佳優(yōu)化設(shè)置,代碼通常臃腫而緩慢。不要依賴默認(rèn)的編譯器設(shè)置。查看編譯器手冊(cè),了解可用于調(diào)整RAM和ROM大小的優(yōu)化和設(shè)置。
結(jié)論
實(shí)時(shí)嵌入式軟件開(kāi)發(fā)人員經(jīng)常為管理他們的系統(tǒng)內(nèi)存而苦惱。由于沒(méi)有跟蹤內(nèi)存的去向,它們可能會(huì)很快耗盡代碼空間,或者出現(xiàn)與堆碎片相關(guān)的運(yùn)行時(shí)問(wèn)題。我們?cè)诒疚闹醒芯康募记煽雌饋?lái)很簡(jiǎn)單,但是通過(guò)遵循它們,嵌入式開(kāi)發(fā)人員不僅可以更好地管理他們的內(nèi)存占用,還可以省去調(diào)試一個(gè)瀕臨內(nèi)存災(zāi)難的系統(tǒng)的麻煩。