標籤:style io os 使用 ar 檔案 資料 sp c
現在新買的安卓千元機都是2G記憶體的了,我們還要絞盡腦汁地省記憶體?是的,那是高端處理器的特色,咱們這裡講的是資源緊缺型的嵌入式系統設計方法。一般主控是單片機控制器的電子產品的成本跟記憶體的關係可是成正比的,尤其在SOC晶片設計時是韌體開發需要重點關注的。大量量產前要確定內建SRAM的大小,而且是在滿足功能需求的情況下越小越好。這就需要考究軟體系統的設計和編程開發的技能了。這裡僅就我個人的工作經驗來總結,涉及的是音視頻多媒體電子產品,類似系統一般都會自行定製作業系統,驅動、中介軟體和應用等模組都有,所謂麻雀雖小,五髒俱全。
一、記憶體塊分時複用
分時複用即對代碼進行分塊(Bank)管理。它的設計需求來源於:
1. 很多電子產品並不是像現在的安卓手機一樣同時跑多個應用,頂多就聽歌時瀏覽圖片而已,非智能手機也是如此。但我們也會看到電子產品裡面有有很多的應用,如設定、電子書、電話本、錄音啊等等。因此,不同時啟動並執行應用佔用同一塊記憶體空間理所當然。
2. 驅動空間。有很多的驅動並不同時使用,如聽FM時是FM驅動,聽歌又是使用解碼器,所以很多驅動也是可以服用同一塊空間的。
3. 中介軟體的複用。如UI、硬體驅動的再次封裝使用等等,其由對應的應用直接調用,一般也存在複用的需求。
4. 資料區段的複用。應用和驅動都有資料,同樣有複用的情境需求。
理論上驅動和代碼也可以服用空間的,但需要考慮的細節太多,而且這樣做擴充性不好,所以應用一般是不會跟驅動複用空間的。一般較為粗糙地將軟體系統分為以下幾個部分:啟動、驅動、作業系統、中介軟體、應用等層次。啟動為一次性執行,不需太多考慮複用的空間。作業系統一般有常駐記憶體的需求,如中斷管理、時間管理、調度管理、模組代碼管理、虛擬檔案系統等等,當然作業系統的一部分功能並不需要常駐記憶體,主要是一些調用頻率較小的一些介面,如驅動裝卸載、應用初始化等模組。不需常駐記憶體的一些介面實現也可以跟驅動複用空間。
咱們不妨比較一下高端 處理器的記憶體管理單元的功能,記憶體管理單元實現記憶體管理有兩個部分,包括硬體TLB模組和軟體的頁表,硬體TLB是自動將虛擬位址的高N位匹配成實體記憶體的高N位,匹配是根據頁表(TLB是頁表的cache)進行。可以認為頁表是虛擬-物理映射的索引表。高端處理器一般所帶的記憶體都是M層級以上,往往是SDRAM,而不是內建 SRAM了。一般也會用支援多進程的作業系統,即同時支援多個應用在跑。而這多個應用能夠使用的虛擬位址空間和物理地址空間都是整個空間(可能會劃掉一部分用於作業系統,linux就是這樣),也就是其在整個地址空間中進行分時複用。 而我們上面所講的代碼分塊管理只是參考了MMU的設計思想,其分塊是在一定的空間中進行的,而且應用和驅動的分塊空間是分開的。
二、代碼分塊的技巧
第一點是分塊管理的需求和大致的原則,但是如何分塊,塊大小的設計極為考慮系統架構師的功力。塊設大了浪費,小了會導致代碼切換頻繁效率低下。既然都是RAM,有時資料可以跟程式碼片段放到同一個塊中,而沒有必要另加一塊資料區塊。當然這些細節需要綜合評估並加以詳細設計。在成本敏感的電子產品中,這些技巧需要努力挖掘發揮。
三、ROM代替RAM
這隻是從成本的角度去節省記憶體資源,有些代碼需要常駐內容,但其內容並不會隨著版本的更新而發生變化,如上節所講作業系統的調度管理等,可以考慮將這些代碼固化到ROM中。理論上作業系統大部分需要常駐記憶體的代碼都可以固化。RAM和ROM同樣大小的成本比大概是6:1,因此使用ROM也能大幅降低成本。
四、系統移植時砍掉不需要的模組
這是作業系統設計人員務必要考慮的。每個產品都有專屬的功能,而底層作業系統具有普適性,在資源緊缺型系統中,砍掉不必要的模組是非常明智的。
五、作業系統定製
也可以稱為改進作業系統,我們所闡述的系統一般都是封閉系統,只要能高效地實現功能,我們可以任意改動系統中所有的代碼。例如對於可執行檔ELF檔案,作業系統如果按標準的流程要解析完ELF檔案再載入,但不僅需要很多的記憶體資料,而且也效率低下。ELF有關載入和執行最重要的就是.CODE、.DATA、.CONST、.BSS等段資訊,我們完全可以離線抽取出來產生一個新的簡單的定製檔案格式,作業系統只需解析這個簡單的檔案就可以了。這樣做不僅節省記憶體,也能節省外儲存空間。
六、 編程技巧
這個需要平時的積累。例如,在變數的排列方面,我們都知道編譯會考慮對齊。
char a;int b; char c;這樣定義變數的次序需要的記憶體是比 char a; char c; int b;要大的。
七、演算法設計
好的演算法一般會是輕巧的,效率高的。
八、代碼編譯最佳化
編譯時間選擇最佳化層級高的,這樣產生代碼大小有有大規模的減小。
九、編譯指令模式
如arm裡面選thumb指令,mips選擇mips16e,這是由體繫結構所決定的,體繫結構也是為了考慮節省代碼空間資源而設計了16位的指令模式,而這些CPU的字長往往是32位。這種方式能減少30%左右的代碼量。
十、 棧空間的規劃
每個線程都會有自己的棧,而每個線程的棧都應該根據其線程的調用深度來具體設定,像UCOS就有一個棧使用率的任務,我們不妨借用這種思路來看看某個線程最終的棧深度。
設定獨立的中斷棧,可以避免每個任務棧都要給中斷預留棧空間。
扁平的函數調用方法用棧一般要比縱向的函數調用小。嵌入式開發有時為了效率和資源,不應該把代碼分塊分得太細,函數一大摞,既增加代碼量和棧,也降低運行效率。
十一、合理規劃記憶體空間,調整好連結檔案,儘可能做到已有物理空間的高度利用。
例如,我們規劃空間時往往程式碼片段和資料區段分開,但實際的程式碼片段可能又用不完,這時就可以把一部分變數定位到程式碼片段之後。
十二、善於利用連結的段屬性
利如uboot的命令格式,每個命令格式都是一個資料結構cmd_tbl_s,有名稱、執行函數、協助提示資訊等等。考慮到命令的管理,我們自然會想到結構數組,但是數組的大小怎麼設定呢?設定大了浪費,設定剛剛夠,那增加一個命令又得改大小。uboot巧妙地運用了連結的段屬性,只要是命令就加上section (".u_boot_cmd"),那所有命令自然就放到這個段裡面了,需要查詢命令就遍曆這個section就可以了。linux裡面大量應用了連結段屬性技術。
節省記憶體的嵌入式軟體設計技巧