這一篇,我們將深入討論內(nèi)存管理,所謂的內(nèi)存管理其實就是動態(tài)內(nèi)存的管理,只有動態(tài)內(nèi)存才會有生命周期,又開始有結(jié)束,他才會有需要管理的問題,例如我們?nèi)祟?,假如是靜態(tài)的內(nèi)存,從被定義開始,他就是永恒存在的,不需要管理。
在C語言中,經(jīng)常使用malloc()和free()函數(shù)來動態(tài)的申請和釋放內(nèi)存,這種方式是基于堆來管理內(nèi)存的方式,這種方式在嵌入式實時系統(tǒng)中,會存在很多問題隱患:
1.頻繁的使用這種方式進行內(nèi)存的分配和釋放,會把堆搞得支離破碎,并因為不能再分配更多的內(nèi)存而導致應用程序崩潰。
2.基于堆得內(nèi)存管理是浪費的,所有的堆管理算法必須為每個被分配的塊維護某些頭部信息,造成內(nèi)存額外的開銷。
3.malloc()和free()函數(shù)在實際申請和釋放內(nèi)存的時候,執(zhí)行的時間是不可確定的,這意味著他們潛在的可能需要一段長的時間,這與我們的實時原則是矛盾的。
然而,堆的問題并不僅僅限于以上這些,在多線程環(huán)境中使用堆時,會引入新的問題,首先堆變成了一個共享資源,這引入了并發(fā)的問題:
1. malloc()和free()函數(shù)是不可重入的,也就是說,他們不能從多個線程被安全的調(diào)用。當然可以通過互斥體來彌補API無法重入的問題,但是涉及到互斥體,你又該考慮線程阻塞的問題以及由于互斥體的加入,是否會造成優(yōu)先級反轉(zhuǎn)的問題,問題似乎變得更復雜了。
2.malloc()和free()是必須成雙入對,當你需要使用時,申請了一塊內(nèi)存,當你使用完該內(nèi)存時,一定要主動地去釋放他,不然它會一直占用該內(nèi)存,變成了靜態(tài)內(nèi)存的存在,相當于在動態(tài)內(nèi)存里面開了一塊靜態(tài)的內(nèi)存,當你只開一塊或者幾塊的時候,也沒問題,怕就怕,某種情況下觸發(fā)了頻繁這樣的去申請內(nèi)存,而在使用完時,又沒有釋放,積少成多,你的堆就被消耗干凈了。
3.與第二種方式相反,當你申請了一塊內(nèi)存以后,在應用程序還沒有使用完的時候,被意外釋放了,那么將會造成dangling指針,當應用程序再次使用該指針時你的應用程序可能崩潰。
4.堆相關的問題是出名的難以測試。
講了這么多堆的缺點,堆有個最大的優(yōu)點,可以申請可變大小的內(nèi)存塊,這是我們接下里介紹的內(nèi)存池的方式無法提供的,凡是總是有利有弊,引入內(nèi)存池是為了解決以上由于使用堆而帶來的那些問題。
為了更簡單,更高性能和更安全的使用動態(tài)內(nèi)存,一個通用的方案是,定義一塊尺寸固定的堆,也被稱為內(nèi)存池,對于QF框架來說,在管理動態(tài)事件時,使用內(nèi)存池可能是一個更好的選擇。
先來看看使用內(nèi)存池的缺點,首先他只能以一種固定尺寸的塊,但是事件的尺寸并不是統(tǒng)一的,所以為了支持所有的事件,塊的尺寸必須被定義為能夠支持的最大尺寸的事件,這樣會造成內(nèi)存的浪費,折中的方案是定義多個不同尺寸的塊的內(nèi)存池,用于支持不同尺寸的事件,QF最大支持三種不同尺寸的內(nèi)存池,你可以定義小中大三種。
接下里說說他的優(yōu)點:
1.內(nèi)尺寸不會因為頻繁的申請和釋放變得碎片化。
2.申請的速度遠快于堆申請速度,僅需臨界區(qū)操作,避免的阻塞的情況。
3.動態(tài)的事件管理完全由QF框架掌控,他自身就有垃圾回收機制,類似java,所以只管申請,不用擔心釋放問題。
事件的管理完全交給了QF框架,那么QF需要知道以下幾件事:
1.這個事件需不需要我管理(動態(tài)還是靜態(tài)事件)。
2.這個事件來自于哪個內(nèi)存池。
3.這個事件在什么情況下被釋放(釋放條件)。 以上這些內(nèi)容在事件被定義時就被確認了,也就是由事件本身來維護,如圖:
關于內(nèi)存池的使用有兩種情況,一種是當QF框架和其它RTOS一起使用時,他可以借用RTOS現(xiàn)有的消息隊列,這種情況后面講到QP+RTOS方案時,我們具體再聊,這里主要介紹一種原生的QF事件隊列,所謂原生就是由QF給出的解決方案。 首先我們來看一下QF是如何管理事件池的,其控制方案由一個控制塊+內(nèi)存池的方案組成:
接下來我們看一下如何實際定義一個內(nèi)存池及如何從內(nèi)存池中申請內(nèi)存:
1.先定義內(nèi)存池。
2.初始化內(nèi)存池。
3.為事件申請內(nèi)存
動態(tài)事件申請內(nèi)存不需要釋放,QF框架會根據(jù)實際情況來回收事件內(nèi)存。