節省內存的嵌入式軟件設計技巧

     如今新買的安卓千元機都是2G內存的了,咱們還要絞盡腦汁地省內存?是的,那是高端處理器的特點,我們這裏講的是資源緊缺型的嵌入式系統設計方法。通常主控是單片機控制器的電子產品的成本跟內存的關係但是成正比的,尤爲在SOC芯片設計時是固件開發須要重點關注的。大量量產前要肯定內置SRAM的大小,並且是在知足功能需求的狀況下越小越好。這就須要考究軟件系統的設計和編程開發的技能了。這裏僅就我我的的工做經驗來總結,涉及的是音視頻多媒體電子產品,相似系統通常都會自行定製操做系統,驅動、中間件和應用等模塊都有,所謂麻雀雖小,五臟俱全。linux

        1、內存塊分時複用算法

         分時複用即對代碼進行分塊(Bank)管理。它的設計需求來源於:編程

       1. 不少電子產品並非像如今的安卓手機同樣同時跑多個應用,頂多就聽歌時瀏覽圖片而已,非智能手機也是如此。但咱們也會看到電子產品裏面有有不少的應用,如設置、電子書、電話本、錄音啊等等。所以,不一樣時運行的應用佔用同一塊內存空間理所固然。數組

       2. 驅動空間。有不少的驅動並不一樣時使用,如聽FM時是FM驅動,聽歌又是使用解碼器,因此不少驅動也是能夠服用同一塊空間的。數據結構

       3. 中間件的複用。如UI、硬件驅動的再次封裝使用等等,其由對應的應用直接調用,通常也存在複用的需求。架構

       4. 數據段的複用。應用和驅動都有數據,一樣有複用的場景需求。函數

       理論上驅動和代碼也能夠服用空間的,但須要考慮的細節太多,並且這樣作擴展性很差,因此應用通常是不會跟驅動複用空間的。通常較爲粗糙地將軟件系統分爲如下幾個部分:啓動、驅動、操做系統、中間件、應用等層次。啓動爲一次性執行,不需太多考慮複用的空間。操做系統通常有常駐內存的需求,如中斷管理、時間管理、調度管理、模塊代碼管理、虛擬文件系統等等,固然操做系統的一部分功能並不須要常駐內存,主要是一些調用頻率較小的一些接口,如驅動裝卸載、應用初始化等模塊。不需常駐內存的一些接口實現也能夠跟驅動複用空間。     優化

         我們不妨比較一下高端 處理器的內存管理單元的功能,內存管理單元實現內存管理有兩個部分,包括硬件TLB模塊和軟件的頁表,硬件TLB是自動將虛擬地址的高N位匹配成物理內存的高N位,匹配是根據頁表(TLB是頁表的cache)進行。能夠認爲頁表是虛擬-物理映射的索引表。高端處理器通常所帶的內存都是M級別以上,每每是SDRAM,而不是內置 SRAM了。通常也會用支持多進程的操做系統,即同時支持多個應用在跑。而這多個應用可以使用的虛擬地址空間和物理地址空間都是整個空間(可能會劃掉一部分用於操做系統,linux就是這樣),也就是其在整個地址空間中進行分時複用。 而咱們上面所講的代碼分塊管理只是參考了MMU的設計思想,其分塊是在必定的空間中進行的,並且應用和驅動的分塊空間是分開的。 spa

       2、代碼分塊的技巧操作系統

       第一點是分塊管理的需求和大體的原則,可是如何分塊,塊大小的設計極爲考慮系統架構師的功力。塊設大了浪費,小了會致使代碼切換頻繁效率低下。既然都是RAM,有時數據能夠跟代碼段放到同一個塊中,而沒有必要另加一塊數據塊。固然這些細節須要綜合評估並加以詳細設計。在成本敏感的電子產品中,這些技巧須要努力挖掘發揮。

       3、ROM代替RAM

       這只是從成本的角度去節省內存資源,有些代碼須要常駐內容,但其內容並不會隨着版本的更新而發生變化,如上節所講操做系統的調度管理等,能夠考慮將這些代碼固化到ROM中。理論上操做系統大部分須要常駐內存的代碼均可以固化。RAM和ROM一樣大小的成本比大概是6:1,所以使用ROM也能大幅下降成本。

       4、系統移植時砍掉不須要的模塊

       這是操做系統設計人員務必要考慮的。每一個產品都有獨有的功能,而底層操做系統具備普適性,在資源緊缺型系統中,砍掉沒必要要的模塊是很是明智的。

       5、操做系統定製

       也能夠稱爲改進操做系統,咱們所闡述的系統通常都是封閉系統,只要能高效地實現功能,咱們能夠任意改動系統中全部的代碼。例如對於可執行的ELF文件,操做系統若是按標準的流程要解析完ELF文件再加載,但不只須要不少的內存資料,並且也效率低下。ELF有關加載和執行最重要的就是.CODE、.DATA、.CONST、.BSS等段信息,咱們徹底能夠離線抽取出來生成一個新的簡單的定製文件格式,操做系統只需解析這個簡單的文件就能夠了。這樣作不只節省內存,也能節省外存儲空間。

       6、 編程技巧

       這個須要平時的積累。例如,在變量的排列方面,咱們都知道編譯會考慮對齊。

       char a;int b; char c;這樣定義變量的次序須要的內存是比 char a; char c; int b;要大的。

      7、算法設計

       好的算法通常會是輕巧的,效率高的。

      8、代碼編譯優化

       編譯時選擇優化級別高的,這樣生成代碼大小有有大規模的減少。

      9、編譯指令模式

      如arm裏面選thumb指令,mips選擇mips16e,這是由體系結構所決定的,體系結構也是爲了考慮節省代碼空間資源而設計了16位的指令模式,而這些CPU的字長每每是32位。這種方式能減小30%左右的代碼量。

  10、 棧空間的規劃

         每一個線程都會有本身的棧,而每一個線程的棧都應該根據其線程的調用深度來具體設定,像UCOS就有一個棧使用率的任務,咱們不妨借用這種思路來看看某個線程最終的棧深度。

        設定獨立的中斷棧,能夠避免每一個任務棧都要給中斷預留棧空間。

        扁平的函數調用方法用棧通常要比縱向的函數調用小。嵌入式開發有時爲了效率和資源,不該該把代碼分塊分得太細,函數一大摞,既增長代碼量和棧,也下降運行效率。

       11、合理規劃內存空間,調整好連接文件,儘量作到已有物理空間的高度利用。

       例如,咱們規劃空間時每每代碼段和數據段分開,但實際的代碼段可能又用不完,這時就能夠把一部分變量定位到代碼段以後。

      12、善於利用連接的段屬性

       利如uboot的命令格式,每一個命令格式都是一個數據結構cmd_tbl_s,有名稱、執行函數、幫助提示信息等等。考慮到命令的管理,咱們天然會想到結構數組,可是數組的大小怎麼設置呢?設置大了浪費,設置剛剛夠,那增長一個命令又得改大小。uboot巧妙地運用了連接的段屬性,只要是命令就加上section (".u_boot_cmd"),那全部命令天然就放到這個段裏面了,須要查詢命令就遍歷這個section就能夠了。linux裏面大量應用了連接段屬性技術。

相關文章
相關標籤/搜索