我發(fā)現(xiàn)許多嵌入式軟件開發(fā)人員都提出了一個(gè)特別有趣的話題,那就是動(dòng)態(tài)內(nèi)存分配——在需要時(shí)獲取內(nèi)存塊,這種看似簡(jiǎn)單的常規(guī)操作帶來了大量問題。這些并不局限于嵌入式開發(fā)——許多桌面應(yīng)用程序都會(huì)出現(xiàn)內(nèi)存泄漏,影響性能,并可能導(dǎo)致系統(tǒng)重新啟動(dòng)。但是,我擔(dān)心嵌入式開發(fā)環(huán)境。
通常不建議將malloc()用于嵌入式應(yīng)用程序的原因有很多:
1.該函數(shù)通常不可重入(線程友好),因此在實(shí)時(shí)操作系統(tǒng)中使用它可能具有挑戰(zhàn)性。
2.它的性能是不確定的(可預(yù)測(cè)的),因此分配內(nèi)存塊所需的時(shí)間可能非??勺?,這在實(shí)時(shí)應(yīng)用程序中是一個(gè)挑戰(zhàn)。
3.內(nèi)存分配可能失敗。
雖然這些都是有效的觀點(diǎn),但它們可能并不像看上去那么重要。
只有從多個(gè)線程調(diào)用函數(shù)時(shí),才存在可重入性問題。編寫可重入的malloc()函數(shù)是完全可行的,但也可以使用標(biāo)準(zhǔn)版本,使重入變得不必要。只需將所有內(nèi)存分配活動(dòng)本地化為單個(gè)任務(wù)。你甚至可以創(chuàng)建一個(gè)任務(wù),其唯一功能是動(dòng)態(tài)內(nèi)存分配;其他任務(wù)只需發(fā)送一條消息,請(qǐng)求分配或釋放內(nèi)存塊。
決定論并非總是必需的。非實(shí)時(shí)應(yīng)用程序是實(shí)時(shí)的,非實(shí)時(shí)應(yīng)用程序并不一定要求其操作的所有部分都具有確定性。
分配失敗可能是一個(gè)問題,但它是可以管理的。如果無法分配請(qǐng)求的內(nèi)存,則malloc()函數(shù)將返回空指針,嵌入式開發(fā)人員必須檢查此響應(yīng)并采取適當(dāng)?shù)拇胧?。如果故障是由于?nèi)存耗盡造成的,則很可能存在設(shè)計(jì)缺陷—沒有為堆分配足夠的內(nèi)存。然而,分配失敗的一個(gè)常見原因是堆碎片;有足夠的可用內(nèi)存,但它不在連續(xù)區(qū)域中。這種碎片的產(chǎn)生是因?yàn)閮?nèi)存以隨機(jī)方式分配和釋放,導(dǎo)致內(nèi)存的分配區(qū)域和空閑區(qū)域。
盡管它不可預(yù)測(cè),malloc()還有另一個(gè)問題——它的速度往往相當(dāng)慢。這并不奇怪,因?yàn)楹瘮?shù)的功能相當(dāng)復(fù)雜?;趬K的分配器的內(nèi)在簡(jiǎn)單性非常有效地解決了這個(gè)問題。
但是,如果應(yīng)用程序在不可預(yù)測(cè)的時(shí)間確實(shí)需要隨機(jī)大小的內(nèi)存塊,該怎么辦?
實(shí)現(xiàn)這種靈活性,同時(shí)避免碎片和不確定性的一種方法是構(gòu)建一個(gè)分配器,根據(jù)請(qǐng)求的內(nèi)存塊大小從多個(gè)“池”中選擇塊。為池選擇塊大小的一個(gè)好方法(如果你事先不知道將需要的塊大小)是使用幾何級(jí)數(shù),如16、32、64、128字節(jié)。然后,分配將如下所示:
顯然,有些分配會(huì)非常有效:16字節(jié)池中有16個(gè)字節(jié),來自32字節(jié)池的31字節(jié);來自16字節(jié)池的9字節(jié);來自128字節(jié)池的65字節(jié)??偟膩碚f,這些低效率對(duì)于速度、決定論和消除碎片化的好處來說只是一個(gè)很小的代價(jià),可以提高嵌入式開發(fā)效率。